Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Testing Ruby with Rspec
- Vysakh Sreenivasan (vysakh0)
I was like this before
This is how I met my
girlfriend
Then this happened :-/
promise me never to execute the program
to see the output i.e ruby file_name.rb
Before we start testing
Pinky promise? Yes?
$ mkdir ruby_testing
$ cd ruby_testing
$ mkdir lib
Open terminal & prepare env
$ bundle init
$ bundle inject rspec, 3.2
$ bundle --binstubs
$ rspec --init
Create Gemfile. Insert & install rspec
4 phases of testing
- Setup
- Exercise
- Verify
- Teardown (Testing framework
does it for us, duh!)
# setup
user = User.new(name: 'yolo')
#expect
user.save
# verify
expect(user.name).not_to be_nil
Wth is expect?
Hmm, it is a matcher?
Describe
the method or class you want to test
# lib/user.rb
class User
end
# spec/user_spec.rb
require ‘spec_helper’
require ‘user’
RSpec.describe User do
end
# lib/sum.rb
def sum(a, b)
a + b
end
# spec/sum_spec.rb
require ‘spec_helper’
require ‘sum’
RSpec.describe ‘#sum’ do
end
Run the specs by the cmd
rspec
# lib/calc.rb
class Calc
def sum(a, b)
a + b
end
end
# spec/calc_spec.rb
require ‘spec_helper’
require ‘calc’
RSpec.descri...
Tip - use Describe strictly for class & methods
- describe “ClassName” do end
- describe “#instance_method” do end
- descr...
describe tells who is tested
We need something to
Tell what we are testing of it
it ‘gives sum of 2’ do
end
# or
it {}
require ‘spec_helper’
require ‘calc’
RSpec.describe Calc do
describe ‘#sum’ do
it “returns sum of 2 numbers” do
end
end
end
Lets apply
4 phases of testing
require ‘spec_helper’
require ‘calc’
RSpec.describe ‘Calc’ do
describe ‘#sum’ do
it “returns sum of 2 numbers” do
calc = C...
require ‘calc’
RSpec.describe Calc do
describe ‘#sum’ do
it “returns sum of 2 numbers” do
calc = Calc.new # setup
expect(c...
For brevity in the slides,
I’m gonna leave the require ‘spec_helper’
Tip - it statement
- don’t use “should” or “should not” in
description
- say about actual functionality, not what
might be...
(lets just take a quick look)
Matchers
expect(num.odd?).to be false
#=> num = 2
expect(user.email).to be_falsey
#=> user.email = nil
expect(num).to be >= 3
#=> n...
expect(str).to match /testing$/
#=> str = “Welcome to testing”
expect(tiger).to be_an_instance_of(Tiger)
#=> tiger.class =...
expect { sum }.to raise_error ArugmentError
#=> sum(a, b)
expect(person).to have_attributes(:name => "Jim", :age => 32)
#=...
expect(list).to start_with(36)
#=> list = [36, 49, 64, 81]
expect(name).to start_with(‘M’)
#=> name = “Matz”
expect(list)....
expect("a string").to include("str")
expect([1, 2]).to include(1, 2)
expect(:a => 1, :b => 2).to include(:a, :b)
expect(:a...
expect([1, 2]).not_to include(1)
expect(name).not_to start_with(‘M’)
#=> name = “DHH
expect(tiger).not_to be_a(Lion)
#=> t...
expect([1, 3, 5]).to all( be_odd )
# it is inclusive by default
expect(10).to be_between(5, 10)
# ...but you can make it e...
expect([1, 2, 3]).to contain_exactly(2, 1, 3)
#=> pass
expect([1, 2, 3]).to contain_exactly(2, 1)
#=> fail
expect([1, 2, 3...
Normal equality expectations do not work well for
floating point values
expect(27.5).to be_within(0.5).of(28.0)
expect(27....
There are aliases for matchers
use it based on the context
a_value > 3 be < 3
a_string_matching(/foo/) match(/foo/)
a_block_raising(ArgumentError) raise_error(ArgumentError)
See thi...
is_expected.to
same as expect(subject).to
require ‘calc’
RSpec.describe Calc do
it { expect(Calc).to respond_to(:sum) }
end
require ‘calc’
RSpec.describe Calc do
it { expect(subject).to respond_to(:sum) }
end
require ‘calc’
RSpec.describe Calc do
it { is_expected.to respond_to(:sum) }
end
Compound matchers
using and, or
expect(str).to start_with(“V”).and end_with(“h”)
#=> str = “Vysakh”
expect(stoplight.color).to eq("red").or eq("green").or eq("yellow")
#=> stoplight.color ⇒ “yellow”
change matcher
# lib/team.rb
class Team
attr_accessor :goals
def score
@goals += 1
end
end
require ‘team’
RSpec.describe Team do
describe ‘#score’ do
it ‘increments goals’ do
team = Team.new
expect { team.score }....
x = y = 0
expect {
x += 1
y += 2
}.to change { x }.to(1).and change { y }.to(2)
Composable matchers
s = "food"
expect { s = "barn" }.to change { s }.
from( a_string_matching(/foo/) ).
to( a_string_matching(/bar/) )
expect(arr).to match [
a_string_ending_with("o"),
a_string_including("e")
]
#=> arr = [“bozo”, “great”]
Magical(predicate) Matchers
expect(0).to be_zero
#=> 0.zero? ⇒ true
expect(2).to be_even
#=> 2.even? ⇒ true
expect(me).to have_job
#=> me.has_job? ⇒ t...
Scenarios of methods or
Contexts
# lib/duh.rb
def duh(num)
if num.odd?
“mumbo”
else
“jumbo”
end
end
require ‘duh’
RSpec.describe ‘#duh’ do
it ‘says mumbo if number is odd’ do
expect(duh(3)).to eq “mumbo”
end
it ‘says jumbo...
Never use if inside it
Instead use context
require ‘duh’
RSpec.describe ‘#duh’ do
context ‘when number is odd’ do
it ‘says mumbo’ do
expect(duh(3)).to eq “mumbo”
end...
Tip - Context
- Always has an opposite negative
case
- So, never use a single context.
- Always begin with “when…”
let helper
require ‘team’
RSpec.describe Team do
describe ‘#score’ do
it ‘increments goals’ do
team = Team.new
expect(team.score).to ...
require ‘team’
RSpec.describe Team do
let(:team) { Team.new }
describe ‘#score’ do
it ‘increments goals’ do
expect(team.sc...
def team
Team.new
end
let(:team) is same as
Is invoked only when it is called
before & after helper
require ‘team.’
RSpec.describe Team do
before do
@team = Team.new
puts “Called every time before the it or specify block”
...
require ‘team’
RSpec.describe Team do
before(:suite) do
puts “Get ready folks! Testing are coming!! :D ”
end
describe ‘#sc...
Types passed to before/after
- :example (runs for each test)
- :context (runs for each context)
- :suite (runs for entire ...
Tip - Use let instead of before
- To create data for the spec
examples.
- let blocks get lazily evaluated
# use this:
let(:article) { FactoryGirl.create(:article) }
# ... instead of this:
before { @article = FactoryGirl.create(:...
Tip: Use before/after for
- actions or
- when the same obj/variable needs to be
used in different examples
before do
@book = Book.new(title: "RSpec Intro")
@customer = Customer.new
@order = Order.new(@customer, @book)
@order.subm...
Use factorygirl to
create test objects
Stubs
class PriceCalculator
def add(product)
products << product
end
def products
@products ||= []
end
def total
@products.map(&...
class Product
attr_reader :price
end
class PriceCalculator
def add(product)
products << product
end
def products
@products...
$ irb
> require ‘rspec/mocks/standalone’
> class User; end
> allow(User).to receive(:wow).and_return(“Yolo”)
> User.wow
=>...
You can also use block to return instead
and_return
allow(User).to receive(:wow) { (“Yolo”) }
3 types of return for wow method
allow(User).to receive(:wow)
.and_return(“yolo”, “lol”, “3rd time”)
Diff output when running diff times
User.wow #=> yolo
User.wow #=> lol
User.wow #=> 3rd time
User.wow #=> 3rd time
User.wo...
So, you could use it as if it is 2 different objects
2.times { calculator.add product_stub }
Diff between double & instance_double
Instance double requires
- class to be defined
- methods to be defined in that class...
Use stubs, mocks, spies
with caution
Skip and Focus tests
Say you have 3 failing tests
xit
- add x to all but one failing it
blocks
- xit blocks will be skipped
You can use xit or skip: true
xit “does this thing” do
end
it “asserts name”, skip: true do
end
it “asserts name”, skip: “...
it “asserts name” do
pending
end
it “asserts name” do
skip
end
You can use skip/pending inside it
Say you have 20 tests, all
passing but very slow
You can use fit to focus specific test
fit “asserts name” do
end
#=> rspec --tag focus
#=> only this block will be run
Another way to focus specific test
it “asserts name”, focus: true do
end
#=> rspec --tag focus
#=> only this block will be...
You can also use the same in describe or
context
fdescribe “#save” do
end
describe “#save”, skip: true do
end
Use subject when possible
describe Article do
subject { FactoryGirl.create(:article) }
it 'is not published on creation' do
expect(subject).not_to b...
Shared examples
RSpec.describe FacebookAPI do
it "has posts" do
expect(FbAPI.new("vysakh0")).to respond_to :posts
end
it_behaves_like("API...
RSpec.shared_examples_for "API" do |api|
it "returns a formatted hash" do
expect(api.profile).to match [
a_hash_including(...
Shared context
RSpec.shared_context "shared stuff" do
before { @some_var = :some_value }
def shared_method
"it works"
end
let(:shared_let...
require "./shared_stuff.rb"
RSpec.describe "#using_shared_stuff'" do
include_context "shared stuff"
it "has access to meth...
Custom Matchers
RSpec::Matchers.define :be_a_multiple_of do |expected|
match do |actual|
actual % expected == 0
end
end
# usage:
expect(9)...
RSpec::Matchers.define :be_a_palindrome do
match do |actual|
actual.reverse == actual
end
end
# usage:
expect(“ror”).to be...
RSpec::Matchers.define :be_bigger_than do |min|
chain :but_smaller_than, :max
match do |value|
value > min && value < max
...
Define negated matcher
RSpec::Matchers.define define_negated_matcher :exclude, :include
# rather than
# expect(odd_numbers).not_to include(12)
ex...
There are lot more
awesomeness!!
- https://relishapp.com/rspec
- https://github.com/reachlocal/rspec-style-
guide
- https://github.com/eliotsykes/rspec-rai...
Carpe Diem
Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)
Upcoming SlideShare
Loading in …5
×

Testing Ruby with Rspec (a beginner's guide)

4,347 views

Published on

To test Ruby with a simple testing framework, rspec. Used Rspec 3.2 in the slides.

Published in: Software

Testing Ruby with Rspec (a beginner's guide)

  1. 1. Testing Ruby with Rspec - Vysakh Sreenivasan (vysakh0)
  2. 2. I was like this before
  3. 3. This is how I met my girlfriend
  4. 4. Then this happened :-/
  5. 5. promise me never to execute the program to see the output i.e ruby file_name.rb Before we start testing
  6. 6. Pinky promise? Yes?
  7. 7. $ mkdir ruby_testing $ cd ruby_testing $ mkdir lib Open terminal & prepare env
  8. 8. $ bundle init $ bundle inject rspec, 3.2 $ bundle --binstubs $ rspec --init Create Gemfile. Insert & install rspec
  9. 9. 4 phases of testing
  10. 10. - Setup - Exercise - Verify - Teardown (Testing framework does it for us, duh!)
  11. 11. # setup user = User.new(name: 'yolo') #expect user.save # verify expect(user.name).not_to be_nil
  12. 12. Wth is expect? Hmm, it is a matcher?
  13. 13. Describe the method or class you want to test
  14. 14. # lib/user.rb class User end # spec/user_spec.rb require ‘spec_helper’ require ‘user’ RSpec.describe User do end
  15. 15. # lib/sum.rb def sum(a, b) a + b end # spec/sum_spec.rb require ‘spec_helper’ require ‘sum’ RSpec.describe ‘#sum’ do end
  16. 16. Run the specs by the cmd rspec
  17. 17. # lib/calc.rb class Calc def sum(a, b) a + b end end # spec/calc_spec.rb require ‘spec_helper’ require ‘calc’ RSpec.describe Calc do describe ‘#sum’ do end end
  18. 18. Tip - use Describe strictly for class & methods - describe “ClassName” do end - describe “#instance_method” do end - describe “.class_method” do end
  19. 19. describe tells who is tested We need something to Tell what we are testing of it
  20. 20. it ‘gives sum of 2’ do end # or it {}
  21. 21. require ‘spec_helper’ require ‘calc’ RSpec.describe Calc do describe ‘#sum’ do it “returns sum of 2 numbers” do end end end
  22. 22. Lets apply 4 phases of testing
  23. 23. require ‘spec_helper’ require ‘calc’ RSpec.describe ‘Calc’ do describe ‘#sum’ do it “returns sum of 2 numbers” do calc = Calc.new # setup result = calc.sum(2, 3) # exercise expect(result).to eql(5) # verify end end end
  24. 24. require ‘calc’ RSpec.describe Calc do describe ‘#sum’ do it “returns sum of 2 numbers” do calc = Calc.new # setup expect(calc.sum(2, 3)).to eql(5) # exercise & verify end end end
  25. 25. For brevity in the slides, I’m gonna leave the require ‘spec_helper’
  26. 26. Tip - it statement - don’t use “should” or “should not” in description - say about actual functionality, not what might be happening - Use only one expectation per example.
  27. 27. (lets just take a quick look) Matchers
  28. 28. expect(num.odd?).to be false #=> num = 2 expect(user.email).to be_falsey #=> user.email = nil expect(num).to be >= 3 #=> num = 5
  29. 29. expect(str).to match /testing$/ #=> str = “Welcome to testing” expect(tiger).to be_an_instance_of(Tiger) #=> tiger.class => Tiger expect(tiger).to be_a(Cat) #=> tiger.class.superclass => Cat
  30. 30. expect { sum }.to raise_error ArugmentError #=> sum(a, b) expect(person).to have_attributes(:name => "Jim", :age => 32) #=> person.name => Jim
  31. 31. expect(list).to start_with(36) #=> list = [36, 49, 64, 81] expect(name).to start_with(‘M’) #=> name = “Matz” expect(list).to end_with(81) #=> list = [36, 49, 64, 81] expect(name).to end_with(‘z’) #=> name = “Matz”
  32. 32. expect("a string").to include("str") expect([1, 2]).to include(1, 2) expect(:a => 1, :b => 2).to include(:a, :b) expect(:a => 1, :b => 2).to include(:a => 1)
  33. 33. expect([1, 2]).not_to include(1) expect(name).not_to start_with(‘M’) #=> name = “DHH expect(tiger).not_to be_a(Lion) #=> tiger.class => Tiger
  34. 34. expect([1, 3, 5]).to all( be_odd ) # it is inclusive by default expect(10).to be_between(5, 10) # ...but you can make it exclusive: checks in range 4..9 expect(10).not_to be_between(5, 10).exclusive # ...or explicitly label it inclusive: expect(10).to be_between(5, 10).inclusive
  35. 35. expect([1, 2, 3]).to contain_exactly(2, 1, 3) #=> pass expect([1, 2, 3]).to contain_exactly(2, 1) #=> fail expect([1, 2, 3]).to match_array [2, 1, 3] #=> pass expect([1, 2, 3]).to match_array [2, 1] #=> fail
  36. 36. Normal equality expectations do not work well for floating point values expect(27.5).to be_within(0.5).of(28.0) expect(27.5).to be_within(0.5).of(27.2) expect(27.5).not_to be_within(0.5).of(28.1) expect(27.5).not_to be_within(0.5).of(26.9)
  37. 37. There are aliases for matchers use it based on the context
  38. 38. a_value > 3 be < 3 a_string_matching(/foo/) match(/foo/) a_block_raising(ArgumentError) raise_error(ArgumentError) See this gist for more aliases https://gist.github.com/JunichiIto/f603d3fbfcf99b914f86 Few matchers and their aliases
  39. 39. is_expected.to same as expect(subject).to
  40. 40. require ‘calc’ RSpec.describe Calc do it { expect(Calc).to respond_to(:sum) } end
  41. 41. require ‘calc’ RSpec.describe Calc do it { expect(subject).to respond_to(:sum) } end
  42. 42. require ‘calc’ RSpec.describe Calc do it { is_expected.to respond_to(:sum) } end
  43. 43. Compound matchers using and, or
  44. 44. expect(str).to start_with(“V”).and end_with(“h”) #=> str = “Vysakh”
  45. 45. expect(stoplight.color).to eq("red").or eq("green").or eq("yellow") #=> stoplight.color ⇒ “yellow”
  46. 46. change matcher
  47. 47. # lib/team.rb class Team attr_accessor :goals def score @goals += 1 end end
  48. 48. require ‘team’ RSpec.describe Team do describe ‘#score’ do it ‘increments goals’ do team = Team.new expect { team.score }.to change(team, :goals).by(1) end end end
  49. 49. x = y = 0 expect { x += 1 y += 2 }.to change { x }.to(1).and change { y }.to(2)
  50. 50. Composable matchers
  51. 51. s = "food" expect { s = "barn" }.to change { s }. from( a_string_matching(/foo/) ). to( a_string_matching(/bar/) )
  52. 52. expect(arr).to match [ a_string_ending_with("o"), a_string_including("e") ] #=> arr = [“bozo”, “great”]
  53. 53. Magical(predicate) Matchers
  54. 54. expect(0).to be_zero #=> 0.zero? ⇒ true expect(2).to be_even #=> 2.even? ⇒ true expect(me).to have_job #=> me.has_job? ⇒ true
  55. 55. Scenarios of methods or Contexts
  56. 56. # lib/duh.rb def duh(num) if num.odd? “mumbo” else “jumbo” end end
  57. 57. require ‘duh’ RSpec.describe ‘#duh’ do it ‘says mumbo if number is odd’ do expect(duh(3)).to eq “mumbo” end it ‘says jumbo if number is not odd’ do expect(duh(4)).to eq “jumbo” end end
  58. 58. Never use if inside it Instead use context
  59. 59. require ‘duh’ RSpec.describe ‘#duh’ do context ‘when number is odd’ do it ‘says mumbo’ do expect(duh(3)).to eq “mumbo” end end context ‘when number is not odd’ do it ‘says jumbo’ do expect(duh(4)).to eq “jumbo” end end end
  60. 60. Tip - Context - Always has an opposite negative case - So, never use a single context. - Always begin with “when…”
  61. 61. let helper
  62. 62. require ‘team’ RSpec.describe Team do describe ‘#score’ do it ‘increments goals’ do team = Team.new expect(team.score).to change(Team.goals).by(1) end end describe ‘#matches_won’ do it ‘gives number of matches won by the team” do team = Team.new expect(team.matches_won).to eq 0 end end end
  63. 63. require ‘team’ RSpec.describe Team do let(:team) { Team.new } describe ‘#score’ do it ‘increments goals’ do expect(team.score).to change(Team.goals).by(1) end end describe ‘#matches_won’ do it ‘gives number of watches won by the team” do expect(team.matches_won).to eq 0 end end end
  64. 64. def team Team.new end let(:team) is same as Is invoked only when it is called
  65. 65. before & after helper
  66. 66. require ‘team.’ RSpec.describe Team do before do @team = Team.new puts “Called every time before the it or specify block” end describe ‘#score’ do it ‘increments goals of the match’ do expect(@team.score).to change(Team.goals).by(1) end it ‘increments total goals of the Team’’ do expect(@team.score).to change(Team.total_goals).by(1) end end end
  67. 67. require ‘team’ RSpec.describe Team do before(:suite) do puts “Get ready folks! Testing are coming!! :D ” end describe ‘#score’ do it ‘increments goals of the match’ do expect(@team.score).to change(Team.goals).by(1) end it ‘increments total goals of the Team’’ do expect(@team.score).to change(Team.total_goals).by(1) end end end
  68. 68. Types passed to before/after - :example (runs for each test) - :context (runs for each context) - :suite (runs for entire suite, only once, see database cleaner gem)
  69. 69. Tip - Use let instead of before - To create data for the spec examples. - let blocks get lazily evaluated
  70. 70. # use this: let(:article) { FactoryGirl.create(:article) } # ... instead of this: before { @article = FactoryGirl.create(:article) }
  71. 71. Tip: Use before/after for - actions or - when the same obj/variable needs to be used in different examples
  72. 72. before do @book = Book.new(title: "RSpec Intro") @customer = Customer.new @order = Order.new(@customer, @book) @order.submit end
  73. 73. Use factorygirl to create test objects
  74. 74. Stubs
  75. 75. class PriceCalculator def add(product) products << product end def products @products ||= [] end def total @products.map(&:price).inject(&:+) end end class Product end describe PriceCalculator do it "allows for method stubbing" do calculator = PriceCalculator.new calculator.add(double(price: 25.4)) calculator.add(double(price: 101)) expect(calculator.total).to eq 126.4 end end #This works even if there is no Product class is defined # in the actual program
  76. 76. class Product attr_reader :price end class PriceCalculator def add(product) products << product end def products @products ||= [] end def total @products.map(&:price).inject(&:+) end end describe PriceCalculator do it "allows for method stubbing" do calculator = PriceCalculator.new calculator.add instance_double("Product", price: 25.4) calculator.add instance_double("Product", price: 101) expect(calculator.total).to eq 126.4 end end # throws and error if a Product class or its methods are # not defined
  77. 77. $ irb > require ‘rspec/mocks/standalone’ > class User; end > allow(User).to receive(:wow).and_return(“Yolo”) > User.wow => “Yolo Yolo”
  78. 78. You can also use block to return instead and_return allow(User).to receive(:wow) { (“Yolo”) }
  79. 79. 3 types of return for wow method allow(User).to receive(:wow) .and_return(“yolo”, “lol”, “3rd time”)
  80. 80. Diff output when running diff times User.wow #=> yolo User.wow #=> lol User.wow #=> 3rd time User.wow #=> 3rd time User.wow #=> 3rd time
  81. 81. So, you could use it as if it is 2 different objects 2.times { calculator.add product_stub }
  82. 82. Diff between double & instance_double Instance double requires - class to be defined - methods to be defined in that class. - Only then a method can be allowed to it.
  83. 83. Use stubs, mocks, spies with caution
  84. 84. Skip and Focus tests
  85. 85. Say you have 3 failing tests
  86. 86. xit - add x to all but one failing it blocks - xit blocks will be skipped
  87. 87. You can use xit or skip: true xit “does this thing” do end it “asserts name”, skip: true do end it “asserts name”, skip: “Bored right now” do end
  88. 88. it “asserts name” do pending end it “asserts name” do skip end You can use skip/pending inside it
  89. 89. Say you have 20 tests, all passing but very slow
  90. 90. You can use fit to focus specific test fit “asserts name” do end #=> rspec --tag focus #=> only this block will be run
  91. 91. Another way to focus specific test it “asserts name”, focus: true do end #=> rspec --tag focus #=> only this block will be run
  92. 92. You can also use the same in describe or context fdescribe “#save” do end describe “#save”, skip: true do end
  93. 93. Use subject when possible
  94. 94. describe Article do subject { FactoryGirl.create(:article) } it 'is not published on creation' do expect(subject).not_to be_published end end
  95. 95. Shared examples
  96. 96. RSpec.describe FacebookAPI do it "has posts" do expect(FbAPI.new("vysakh0")).to respond_to :posts end it_behaves_like("API", FbAPI.new(“vysakh0”)) end Rspec.describe TwitterAPI do it "has tweets" do expect(TwitterAPI.new("vysakh0")).to respond_to :tweets end it_behaves_like("API", TwitterAPI.new(“vysakh0”)) end
  97. 97. RSpec.shared_examples_for "API" do |api| it "returns a formatted hash" do expect(api.profile).to match [ a_hash_including( name: an_instance_of(String), category: an_instance_of(String), price: an_instance_of(Float)) ] end end
  98. 98. Shared context
  99. 99. RSpec.shared_context "shared stuff" do before { @some_var = :some_value } def shared_method "it works" end let(:shared_let) { {'arbitrary' => 'object'} } subject do 'this is the subject' end end
  100. 100. require "./shared_stuff.rb" RSpec.describe "#using_shared_stuff'" do include_context "shared stuff" it "has access to methods defined in shared context" do expect(shared_method).to eq("it works") end end
  101. 101. Custom Matchers
  102. 102. RSpec::Matchers.define :be_a_multiple_of do |expected| match do |actual| actual % expected == 0 end end # usage: expect(9).to be_a_multiple_of(3)
  103. 103. RSpec::Matchers.define :be_a_palindrome do match do |actual| actual.reverse == actual end end # usage: expect(“ror”).to be_a_palindrome
  104. 104. RSpec::Matchers.define :be_bigger_than do |min| chain :but_smaller_than, :max match do |value| value > min && value < max end end # usage: expect(10).to be_bigger_than(5).but_smaller_than(15)
  105. 105. Define negated matcher
  106. 106. RSpec::Matchers.define define_negated_matcher :exclude, :include # rather than # expect(odd_numbers).not_to include(12) expect((odd_numbers).to exclude(12) # user_a = User.new(“A”); user_b = User.new(“B”) # users = [user_a, user_b] expect(users).to include(user_a).and exclude(user_b)
  107. 107. There are lot more awesomeness!!
  108. 108. - https://relishapp.com/rspec - https://github.com/reachlocal/rspec-style- guide - https://github.com/eliotsykes/rspec-rails- examples (rspec-rails) - http://betterspecs.org/ - Resources
  109. 109. Carpe Diem

×