Testing With Rspec Slides
Transcript of Testing With Rspec Slides
-
7/27/2019 Testing With Rspec Slides
1/102
-
7/27/2019 Testing With Rspec Slides
2/102
Introduction L EV EL 1
-
7/27/2019 Testing With Rspec Slides
3/102
Popular Ruby testing framework
Thriving community
Less Ruby-like, natural syntax
Well-formatted output
RSpec
DavidChelimsky
-
7/27/2019 Testing With Rspec Slides
4/102
Describe your applications behavior
Can be done with other frameworks
BDD is not required
But encouraged by the RSpec syntax
And RSpec makes it elegant and readable
Behavior-Driven Dev
-
7/27/2019 Testing With Rspec Slides
5/102
Installing RSpec
$...
Successfully installed rspec-core
Successfully installed rspec-expectations
Successfully installed rspec-mocks
Successfully installed rspec
4 gems installed
gem install rspec
$ rspec --init
create spec/spec_helper.rb
create .rspec
more about configuration in level 2
in your project directory
-
7/27/2019 Testing With Rspec Slides
6/102
describe "A Zombie"do
Describe block
# your examples (tests) go hereend
spec/lib/zombie_spec.rb
require"spec_helper"
_spec.rb
Our project source will live in /lib/zombie.rb
but lets write a test first
-
7/27/2019 Testing With Rspec Slides
7/102
Describe it
describe "A Zombie"do name of the exampleit "is named Ash"end
examples are declared using the it method
require"spec_helper"
spec/lib/zombie_spec.rb
-
7/27/2019 Testing With Rspec Slides
8/102
it "is named Ash"
describe "A Zombie"do
end
Pending
*
Pending:
Zombie is named "Ash"
# Not yet implemented
# ./spec/lib/zombie_spec.rb:17
Finished in 0.00028 seconds
1 example, 0 failures, 1 pending
rspec spec/lib/zombie_spec.rb Pending$
require"spec_helper"
spec/lib/zombie_spec.rb
-
7/27/2019 Testing With Rspec Slides
9/102
end
it "is named Ash"
Describe Class
we dont have a Zombie class yet
zombie_spec.rb:16:in `':uninitialized constant Zombie (NameError)
spec/lib/zombie_spec.rb
class we want to test
rspec spec/lib/zombie_spec.rb$
describe Zombiedorequire"spec_helper"
Failing
-
7/27/2019 Testing With Rspec Slides
10/102
end
it "is named Ash"
Creating the class
require"zombie"
spec/lib/zombie_spec.rb
describe Zombiedo
classZombie
end
lib/zombie.rb
*
Pending:
Zombie is named "Ash" # Not yet implemented
# ./spec/lib/zombie_spec.rb:17
Finished in 0.00028 seconds
1 example, 0 failures, 1 pending
rspec spec/lib/zombie_spec.rb
Pending$
require"spec_helper"
-
7/27/2019 Testing With Rspec Slides
11/102
Expectationsspec/lib/zombie_spec.rb
end
end
describe Zombiedo it "is named Ash" do
zombie.name.should =='Ash'
zombie =Zombie.new
expectation This is how you assert in RSpec
Assertions are called expectations They read more like plain English
assert_equal 'Ash', zombie.name
Test::Unit
-
7/27/2019 Testing With Rspec Slides
12/102
rspec spec/lib/zombie_spec.rb$
Test properly failsspec/lib/zombie_spec.rb
end
end
describe Zombiedo it "is named Ash"
zombie.name.should =='Ash'
zombie =Zombie.new
1) Zombie is named "Ash"
Failure/Error: zombie.name.should == 'Ash'NoMethodError:
undefined method `name' for #
Finished in 0.00125 seconds
1 example, 1 failure
do
-
7/27/2019 Testing With Rspec Slides
13/102
rspec spec/lib/zombie_spec.rb$
Make it pass
.
Finished in 0.00047 seconds1 example, 0 failures
Passed!
lib/zombie.rbspec/lib/zombie_spec.rb
end
end
describe Zombiedo it "is named Ash"
zombie.name.should =='Ash'
zombie =Zombie.new
doclassZombie
end
definitialize
@name='Ash'
end
attr_accessor:name
-
7/27/2019 Testing With Rspec Slides
14/102
zombie =Zombie.new
zombie.brains.should
-
7/27/2019 Testing With Rspec Slides
15/102
lib/zombie.rb
zombie =Zombie.new
zombie.brains.should
-
7/27/2019 Testing With Rspec Slides
16/102
Other matchers
zombie.height.should >5
zombie.height.should >=5
zombie.height.should
-
7/27/2019 Testing With Rspec Slides
17/102
Testing a predicate/lib/zombie.rb
classZombie defhungry?
true
end
end
describe Zombiedo
/spec/lib/zombie_spec.rb
it 'is hungry'dozombie =Zombie.new
zombie.hungry?.should ==trueend
end could read better
predicate
-
7/27/2019 Testing With Rspec Slides
18/102
Predicate be
zombie.hungry?.should be_true
predicate matcher
describe Zombiedo
/spec/lib/zombie_spec.rb
it 'is hungry'dozombie =Zombie.newzombie.hungry?.should ==true
end
end
zombie.should be_hungry
-
7/27/2019 Testing With Rspec Slides
19/102
.
Finished in 0.00045 seconds1 example, 0 failures
Still passing!rspec spec/lib/zombie_spec.rb$
Test passes
describe Zombiedoit 'is hungry'dozombie =Zombie.new
end
end
zombie.should be_hungry
/spec/lib/zombie_spec.rb
-
7/27/2019 Testing With Rspec Slides
20/102
To mark as pending
xit "is named Ash" do
...
end
it "is named Ash" do
pending
...
end
it "is named Ash"
to-do list
Useful for Debugging
-
7/27/2019 Testing With Rspec Slides
21/102
Configuration
& Matchers L EV EL 2
-
7/27/2019 Testing With Rspec Slides
22/102
rspec --init
Installing RSpec
$
Fetching: rspec-core.gem (100%)
Fetching: rspec-expectations.gem (100%)
Fetching: rspec-mocks.gem (100%)
Fetching: rspec.gem (100%)
Successfully installed rspec-core
Successfully installed rspec-expectations
Successfully installed rspec-mocks
Successfully installed rspec
4 gems installed
gem install rspec
in your project directory
create spec/spec_helper.rb
create .rspec
$
-
7/27/2019 Testing With Rspec Slides
23/102
Inside Railsgroup :development, :testdo
gem'rspec-rails'end
...
Installing rspec-core
Installing rspec-expectationsInstalling rspec-mocks
Installing rspec
Installing rspec-rails
bundle install$
create .rspeccreate spec/spec_helper.rb
$
different with Railsrails generate rspec:install
in your Gemfile
not just rspec core
-
7/27/2019 Testing With Rspec Slides
24/102
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
...
RSpec.configure do |config|
config.mock_with :mocha
...
end
Configuration
allows you to change the default mocking framework
spec/spec_helper.rb
requires all helper files within spec/support
-
7/27/2019 Testing With Rspec Slides
25/102
.
Finished in 0.00176 seconds1 examples, 0 failures
rspec --color spec/models/zombie_spec.rb
Output
Zombie
is invalid without a name.
Finished in 0.00052 seconds1 examples, 0 failures
.rspec
--color
--format documentation
$
rspec --color --format documentation spec/models/zombie_spec.rb$
makes it a default
-
7/27/2019 Testing With Rspec Slides
26/102
rspec
rspec spec/models/
rspec spec/models/zombie_spec.rb
rspec spec/models/zombie_spec.rb:4
Running specs
$
$
running a specific directory
running a specific line
$
running all the _spec.rb files within /spec
$
running a specific test
M l
-
7/27/2019 Testing With Rspec Slides
27/102
Model spec
app/models/zombie.rb
classZombie < ActiveRecord::Base
validates :name, presence:true
end
.
Finished in 0.00176 seconds1 examples, 0 failures
spec/models/zombie_spec.rb
rspec spec/models/zombie_spec.rb
require'spec_helper'
describe Zombiedo
it 'is invalid without a name'do
zombie =Zombie.new
zombie.should_not be_valid
end
end
$
predicate matcher
M
-
7/27/2019 Testing With Rspec Slides
28/102
takes a regular expression
describe Zombiedo
it "has a name that matches 'Ash Clone'"do
zombie =Zombie.new(name: "Ash Clone 1")
zombie.name.should match(/Ash Clone \d/)
end
end
spec/models/zombie_spec.rb
Matchers: match
M l
-
7/27/2019 Testing With Rspec Slides
29/102
Matchers: include
describe Zombiedo
it 'include tweets'do
tweet1 = Tweet.new(status: 'Uuuuunhhhhh')
tweet2 = Tweet.new(status: 'Arrrrgggg')
zombie =Zombie.new(name: 'Ash', tweets: [tweet1, tweet2])
zombie.tweets.should include(tweet1)
zombie.tweets.should include(tweet2) end
end
matching on an array
is this Tweet inside tweets?
spec/models/zombie_spec.rb
M
-
7/27/2019 Testing With Rspec Slides
30/102
describe Zombiedo
it 'starts with two weapons'do
zombie =Zombie.new(name: 'Ash')
zombie.should have(2).weapons end
end
describe Zombiedo
it 'starts with two weapons'do
zombie =Zombie.new(name: 'Ash')
zombie.weapons.count.should ==2
Matchers: have
end
end
reads much better
have(n)
have_at_least(n)
have_at_most(n)
these will work too
spec/models/zombie_spec.rb
M
-
7/27/2019 Testing With Rspec Slides
31/102
Matchers: change
describe Zombiedo
it 'changes the number of Zombies'do
zombie =Zombie.new(name:'Ash')
expect { zombie.save }.to change { Zombie.count }.by(1)
end
end
by(n)
from(n)to(n)
give these a shotresults are compared
.from(1).to(5)
spec/models/zombie_spec.rb
you can chain them
runs before and after expect
-
7/27/2019 Testing With Rspec Slides
32/102
ActiveRecord::RecordInvalid
)
endend
describe Zombiedo
it 'raises an error if saved without a name'do
zombie =Zombie.new
raise_error
to
not_to
to_not
these modifiers also work
zombie.save!expect { }.to raise_error(
spec/models/zombie_spec.rb
optionally pass in an exception
M
-
7/27/2019 Testing With Rspec Slides
33/102
More matchersrespond_to(:)
be_within().of()
exist
satisfy { }
be_kind_of()
be_an_instance_of()
M
-
7/27/2019 Testing With Rspec Slides
34/102
@zombie.should satisfy { |zombie| zombie.hungry?}
More [email protected] respond_to(:hungry?)
@width.should be_within(0.1).of(33.3)
@zombie.should exist
@hungry_zombie.should be_kind_of(Zombie)
@status.should be_an_instance_of(String)
HungryZombie < Zombie
-
7/27/2019 Testing With Rspec Slides
35/102
DRY Specs L EV EL 3
DontRepeat
Yourself
l S
-
7/27/2019 Testing With Rspec Slides
36/102
describe Zombiedoit 'responds to name'do
zombie =Zombie.new
zombie.should respond_to(:name)
end
end
Implicit Subjectspec/models/zombie_spec.rb
describe Zombiedo
it 'responds to name'do
subject.should respond_to(:name)
endend subject = Zombie.newonly works using describe with a class
li i i
-
7/27/2019 Testing With Rspec Slides
37/102
Implicit Receiver
describe Zombiedoit 'responds to name'do
subject.should respond_to(:name)
end
end
spec/models/zombie_spec.rb
describe Zombiedo
it 'responds to name' doshould respond_to(:name)
end
end
i i h
-
7/27/2019 Testing With Rspec Slides
38/102
it without name
describe Zombiedo
it { should respond_to(:name) }
end
Zombie
should respond to #name
Finished in 0.00125 seconds
1 examples, 0 failures
rspec spec/lib/zombie_spec.rb$
example name created automatically
describe Zombiedo
it 'responds to name' should respond_to(:name)end
{ }
i
-
7/27/2019 Testing With Rspec Slides
39/102
its
describe Zombiedo
it { subject.name.should =='Ash' }
end
spec/lib/zombie_spec.rb
Zombie
should == "Ash"
Finished in 0.00057 seconds
1 examples, 0 failures
rspec spec/lib/zombie_spec.rb$
describe Zombiedo
its(:name) { should =='Ash' }end
it x l
-
7/27/2019 Testing With Rspec Slides
40/102
its examples
describe Zombiedo
its(:name) { should =='Ash' }
end
its(:weapons) { should include(weapon) }its(:brain) { should be_nil }
its('tweets.size') { should ==2 }
N ti x l
-
7/27/2019 Testing With Rspec Slides
41/102
describe Zombiedo
it 'craves brains when hungry'
it 'with a veggie preference still craves brains when hungry'
it 'with a veggie preference prefers vegan brains when hungry'
end
Nesting examplesspec/models/zombie_spec.rb
Duplication!describe Zombiedo
it 'craves brains when hungry'
describe'with a veggie preference' do
it 'still craves brains when hungry'it 'prefers vegan brains when hungry'
end
end
N ti x l
-
7/27/2019 Testing With Rspec Slides
42/102
Nesting examples
describe Zombiedo
describe'when hungry' do
it 'craves brains'
describe'with a veggie preference' do
it 'still craves brains'
it 'prefers vegan brains'
end
end
end
Duplication!
describe Zombiedo
it 'craves brains when hungry'
describe'with a veggie preference' do
it 'still craves brains when hungry'
it 'prefers vegan brains when hungry'
end
end
t xt
-
7/27/2019 Testing With Rspec Slides
43/102
contextdescribe Zombiedo
describe'when hungry' do
it 'craves brains'
describe'with a veggie preference' do
it 'still craves brains'
it 'prefers vegan brains'
end
endend context instead of describedescribe Zombiedo
context'when hungry' do
it 'craves brains'
context'with a veggie preference' do
it 'still craves brains'
it 'prefers vegan brains'
end
end
end
bj t i t xt
-
7/27/2019 Testing With Rspec Slides
44/102
subject in context
spec/models/zombie_spec.rb
its(:craving) { should =='vegan brains' }
subject { Zombie.new(vegetarian:true) }context'with a veggie preference' do
it 'craves vegan brains' do
craving.should =='vegan brains'
end
end
...
U i bj t
-
7/27/2019 Testing With Rspec Slides
45/102
Using subjectspec/models/zombie_spec.rb
unclear what subject is,especially with many tests
its(:weapons) { should include(axe) }
let(:axe) { Weapon.new(name:'axe') }
it 'can use its axe'dosubject.swing(axe).should ==true
end
end
subject { Zombie.new(vegetarian: true, weapons: [axe]) }context'with a veggie preference' do
N i the bje t
-
7/27/2019 Testing With Rspec Slides
46/102
Naming the subjectspec/models/zombie_spec.rb
its(:weapons) { should include(axe) }
it 'can use its axe'do
.swing(axe).should ==true
end
end
subject
context'with a veggie preference' do
zombie
{ zombie }
let(:zombie)
let(:axe) { Weapon.new(name:'axe') }{ Zombie.new(vegetarian: true, weapons: [axe]) }
Ne bje t t x
-
7/27/2019 Testing With Rspec Slides
47/102
New subject syntaxspec/models/zombie_spec.rb
context'with a veggie preference' do
...
newer syntax
let(:axe) { Weapon.new(name:'axe') }
{ Zombie.new(vegetarian: true, weapons: [axe]) })
context'with a veggie preference' dosubject(:zombie)
subject { zombie }
let(:zombie)
let(:axe) { Weapon.new(name:'axe') }{ Zombie.new(vegetarian: true, weapons: [axe]) }
te b te bje t
-
7/27/2019 Testing With Rspec Slides
48/102
step by step subject
1. example begins to run
2. needs to know subject3. zombie gets created
this is an example of Lazy Evaluationit "creates a zombie" { Zombie.count ==1 }wouldnt work!
subject { zombie }
its(:name) { should be_nil? }
...
end
{ Zombie.create }(:zombie)let
describe Zombiedo
Let every ti e
-
7/27/2019 Testing With Rspec Slides
49/102
Let every time
Will create zombiebefore every example
subject { zombie }
its(:name) { should be_nil? }
...
end
{ Zombie.create }(:zombie)let
describe Zombiedo
!
L t r f t r
-
7/27/2019 Testing With Rspec Slides
50/102
Lets refactordescribe Zombiedo
@zombie= [email protected] be_nil?
end
it 'has no name'do
it 'craves brains'do
@zombie = Zombie.create
Zombie.create@zombie =
should be_craving_brainszombie.@
end
it 'should not be hungry after eating brains'do
@zombie.hungry.should be_true
@zombie.eat(:brains)@zombie.hungry@ .should be_false
end
end
L t r f t r
-
7/27/2019 Testing With Rspec Slides
51/102
Let s refactordescribe Zombiedo
Zombie.create
zombie.name.should be_nil?
end
it 'has no name'do
it 'craves brains'do
Zombie.createZombie.create
should be_craving_brainszombie.
end
it 'should not be hungry after eating brains'dozombie.hungry.should be_true
zombie.eat(:brains)
zombie.hungry.should be_false
end
end
let(:zombie) { }subject { zombie }
L t r f t r
-
7/27/2019 Testing With Rspec Slides
52/102
Let s refactordescribe Zombiedo
Zombie.create
be_nil?it
Zombie.createZombie.create
should be_craving_brains
it 'should not be hungry after eating brains'dozombie.hungry.should be_true
zombie.eat(:brains)
zombie.hungry.should be_false
endend
let(:zombie) { }subject { zombie }
it
s(:name) { should }
{ }
Let ref t r
-
7/27/2019 Testing With Rspec Slides
53/102
Let s refactordescribe Zombiedo
Zombie.create
be_nil?it
Zombie.createZombie.create
should be_craving_brains
it 'should not be hungry after eating brains'do
zombie.hungry
true
zombie.eat(:brains)
zombie.hungry
false
endend
let(:zombie) { }subject { zombie }
it
s(:name) { should }
{ }
expect { }.to change {
}.from( ).to( )
-
7/27/2019 Testing With Rspec Slides
54/102
Hooks & Tags L EV EL 4
Hooks
-
7/27/2019 Testing With Rspec Slides
55/102
end
end
describe Zombiedo
let(:zombie){ Zombie.new }
zombie.should be_hungry
zombie.should be_craving_brains
it 'craves brains'do
end
it 'is hungry'dozombie.hungry!
zombie.hungry!
Hooksspec/models/zombie_spec.rb
dont repeat yourself
Hooks
-
7/27/2019 Testing With Rspec Slides
56/102
end
end
before { zombie.hungry!zombie.hungry!
Hooks
before(:all)
before(:each)run before each example
after(:each)
after(:all)
run once before all
run after each
run after all
spec/models/zombie_spec.rb
describe Zombiedo
let(:zombie){ Zombie.new }
zombie.should be_hungry
zombie.should be_craving_brains
end
it 'is hungry'do
it 'craves brains'do
}
Hooks in context
-
7/27/2019 Testing With Rspec Slides
57/102
end
end
zombie.hungry!
zombie.hungry!
end
end
Hooks in contextspec/models/zombie_spec.rb
describe Zombiedo
let(:zombie){ Zombie.new }
zombie.should be_craving_brains
it 'craves brains'do
context'with a veggie preference' do
it 'still craves brains' do
zombie.vegetarian = true
...
it 'craves vegan brains' do
zombie.vegetarian = true
...
before { zombie.hungry! }
...
Hooks in context
-
7/27/2019 Testing With Rspec Slides
58/102
end
end
end
end
describe Zombiedo
let(:zombie){ Zombie.new }
zombie.should be_craving_brains
it 'craves brains'do
Hooks in context
it 'still craves brains' do
zombie.vegetarian = true
...
...
it 'craves vegan brains' dozombie.vegetarian = true
...
before { }
spec/models/zombie_spec.rb
...
context'with a veggie preference' do
zombie.hungry!zombie.hungry!zombie.hungry!available within contexts
Hooks in context
-
7/27/2019 Testing With Rspec Slides
59/102
...
...
...
it 'still craves brains' do
it 'craves vegan brains' do
zombie.vegetarian = true before { }
end
end
end
end
describe Zombiedo
let(:zombie){ Zombie.new }
zombie.should be_craving_brains
it 'craves brains'do
Hooks in context
before { }
spec/models/zombie_spec.rb
...
context'with a veggie preference' do
zombie.hungry!zombie.hungry!zombie.hungry!
available to all examples
within this context
Shared examples
-
7/27/2019 Testing With Rspec Slides
60/102
end
end
it 'should not have a pulse'do zombie = Zombie.new
zombie.
it 'should not have a pulse'do
vampire = Vampire.new
vampire.
describe Vampiredo
describe Zombiedo
Shared examples
Duplication!spec/models/vampire_spec.rb
spec/models/zombie_spec.rb
pulse.should == false
pulse.should == false
end
end
Shared examples
-
7/27/2019 Testing With Rspec Slides
61/102
it_behaves_like 'the undead'
it_behaves_like 'the undead'
end
end
spec/models/vampire_spec.rb
Shared examples
spec/support/shared_examples_for_undead.rb
used to call shared examples
refers to the implicit subject
spec/models/zombie_spec.rb
describe Vampiredo
pulse.should == false
lets build our shared example
shared_examples_for 'the undead'do
subject.
it 'does not have a pulse'do
pulse.should == false
describe Zombiedo
end
end
Shared examples
-
7/27/2019 Testing With Rspec Slides
62/102
do
Shared examplesspec/models/zombie_spec.rb
spec/support/shared_examples_for_undead.rb
end
end
shared_examples_for 'the undead'do
it 'does not have a pulse'do
let(:undead) {Zombie.new }
describe Zombiedo
it_behaves_like 'the undead'
end
end
pulse.should == falseundead.
we can access the undead we defined in let
Shared examples
-
7/27/2019 Testing With Rspec Slides
63/102
, Zombie.new
Shared examples
spec/support/shared_examples_for_undead.rb
spec/models/zombie_spec.rb
it_behaves_like 'the undead'
describe Zombiedo
end
end
end
pulse.should == falseundead.
|undead|shared_examples_for 'the undead'do
it 'does not have a pulse'do
Metadata and filters
-
7/27/2019 Testing With Rspec Slides
64/102
describe Zombiedo
context'when hungry' doit 'wants brains'
context'with a veggie preference',
it 'still craves brains'
it 'prefers vegan brains'
endend
end
dofocus: true
,vegan: true
Metadata and filters
Zombiewith a veggie preference
still craves brains
prefers vegan brains
Finished in 0.00125 seconds
2 examples, 0 failures
rspec --tag focus spec/lib/zombie_spec.rb$
run only :focus examples
spec/models/zombie_spec.rb
only :focus examples ran
Metadata and filters
-
7/27/2019 Testing With Rspec Slides
65/102
Metadata and filters
RSpec.configure do |config|config.filter_run focus: true
config.run_all_with_everything_filtered =true
...
end
spec/spec_helper.rb
runs everything if none match
spec/models/zombie_spec.rb
describe Zombiedo
context'when hungry' doit 'wants brains'
context'with a veggie preference',
it 'still craves brains'
it 'prefers vegan brains'
endend
end
dofocus: true
,vegan: true
Metadata and filters
-
7/27/2019 Testing With Rspec Slides
66/102
Metadata and filters
Zombie
with a veggie preference
still craves brains
prefers vegan brains
Finished in 0.00125 seconds
2 examples, 0 failures
rspec spec/lib/zombie_spec.rb$
still filtered to :focus
spec/models/zombie_spec.rb
describe Zombiedo
context'when hungry' doit 'wants brains'
context'with a veggie preference',
it 'still craves brains'
it 'prefers vegan brains'
endend
end
dofocus: true
,vegan: true
Metadata and filters
-
7/27/2019 Testing With Rspec Slides
67/102
slow: true
describe Zombiedo
context'when hungry' doit 'wants brains'
context'with a veggie preference',
it 'still craves brains'
it 'prefers vegan brains'
endend
end
do
Metadata and filters
Zombiewants brains
Finished in 0.00125 seconds
1 examples, 0 failures
rspec --tag ~slow spec/lib/zombie_spec.rb$
skip slow examples
spec/models/zombie_spec.rb
slow examples didnt run
Metadata and filters
-
7/27/2019 Testing With Rspec Slides
68/102
spec/spec_helper.rb
Metadata and filters
RSpec.configure do |config|
config.filter_run_excluding slow: true
config.run_all_with_everything_filtered =true
...
end
skip slow examples in default runs
spec/models/zombie_spec.rb
describe Zombiedo
context'when hungry' doit 'wants brains'
context'with a veggie preference',
it 'still craves brains'
it 'prefers vegan brains'
endend
end
doslow: true
Metadata and filters
-
7/27/2019 Testing With Rspec Slides
69/102
rspec --tag slow spec/lib/zombie_spec.rb
Metadata and filters
Zombiewith a veggie preference
still craves brains
prefers vegan brains
Finished in 0.00125 seconds
2 examples, 0 failures
spec/models/zombie_spec.rb
describe Zombiedo
context'when hungry' doit 'wants brains'
context'with a veggie preference',
it 'still craves brains'
it 'prefers vegan brains'
endend
end
doslow: true
$
still filtered to :focus
-
7/27/2019 Testing With Rspec Slides
70/102
Mocking &
Stubbing L EV EL 5
Why stub is needed
-
7/27/2019 Testing With Rspec Slides
71/102
Why stub is needed
/app/models/zombie.rbclassZombie < ActiveRecord::Base
has_one:weapon
defdecapitate
weapon.slice(self, :head) self.status ="dead again"
end
end
zombie weapon
decapitatedefslice(args*) # complex stuff
end
weneedtofakethiscall
Stubs & Mocks
-
7/27/2019 Testing With Rspec Slides
72/102
Stubs & Mocks
StubFor replacing a method with codethat returns a speci!ed result.
Mock
A stub with an expectations that themethod gets called.
Stubbing
-
7/27/2019 Testing With Rspec Slides
73/102
Stubbingzombie
decapitate
weapon
defslice(*args) returnnil
end
/app/models/zombie.rbclassZombie < ActiveRecord::Base
has_one:weapon
defdecapitate
weapon.slice(self, :head)
self.status ="dead again"
end
end
zombie.weapon.stub(:slice)
Example with stub
-
7/27/2019 Testing With Rspec Slides
74/102
Example with stub
/spec/models/zombie_spec.rb
it "sets status to dead again"do
zombie.decapitate
zombie.status.should == "dead again"
end
zombie.weapon.stub(:slice)
describe Zombiedo
let(:zombie) { Zombie.create }
end
context "#decapitate"do
end
defdecapitate
weapon.slice(self, :head)
self.status ="dead again" end
Missing example
-
7/27/2019 Testing With Rspec Slides
75/102
Missing example
it "calls weapon.slice"do
endend
/spec/models/zombie_spec.rb
describe Zombiedolet(:zombie) { Zombie.create }
context "#decapitate"do
it "sets status to dead again"do
zombie.decapitate
zombie.status.should == "dead again"end
zombie.weapon.stub(:slice)
zombie.decapitate
end
Mocking
-
7/27/2019 Testing With Rspec Slides
76/102
Mockingzombie
decapitate
weapon
defslice(*args) returnnil
end
/app/models/zombie.rbclassZombie < ActiveRecord::Base
has_one:weapon
defdecapitate
weapon.slice(self, :head)
self.status ="dead again"
end
end
zombie.weapon.should_receive(:slice)
Complete Spec
-
7/27/2019 Testing With Rspec Slides
77/102
Complete Spec
zombie.weapon.should_receive(:slice)
/spec/models/zombie_spec.rb
it "calls weapon.slice"do
endend
describe Zombiedolet(:zombie) { Zombie.create }
context "#decapitate"do
it "sets status to dead again"do
zombie.decapitate
zombie.status.should == "dead again"end
zombie.weapon.stub(:slice)
zombie.decapitate
end
Mocking with param
-
7/27/2019 Testing With Rspec Slides
78/102
Mocking with param
/spec/models/zombie_spec.rb
/app/models/zombie.rb
.with(zombie.graveyard)
zombie.geolocate
end
classZombie < ActiveRecord::Basedefgeolocate
Zoogle.graveyard_locator(self.graveyard)end
end
it "calls Zoogle.graveyard_locator"do
Zoogle.should_receive(:graveyard_locator)
Mock and return
-
7/27/2019 Testing With Rspec Slides
79/102
Mock and return
/spec/models/zombie_spec.rb
.and_return({latitude:2, longitude:3})
/app/models/zombie.rbdefgeolocate
loc =Zoogle.graveyard_locator(self.graveyard) "#{loc[:latitude]}, #{loc[:longitude]}"
end
it "calls Zoogle.graveyard_locator"do
Zoogle.should_receive(:graveyard_locator).with(zombie.graveyard)
zombie.geolocate
end
Stub and return
-
7/27/2019 Testing With Rspec Slides
80/102
Stub and return
Zoogle.stub(:graveyard_locator).with(zombie.graveyard)
.and_return({latitude:2, longitude:3})
zombie.geolocate.should == "2, 3"
end
it "returns properly formatted lat, long"do/spec/models/zombie_spec.rb
/app/models/zombie.rbdefgeolocate
loc =Zoogle.graveyard_locator(self.graveyard) "#{loc[:latitude]}, #{loc[:longitude]}"
end
Stub object
-
7/27/2019 Testing With Rspec Slides
81/102
Stub obj ct
/spec/models/zombie_spec.rb
/app/models/zombie.rb
it "returns properly formatted lat, long"do
loc =stub(latitude:2, longitude:3)
Zoogle.stub(:graveyard_locator).returns(loc)
zombie.geolocate_with_object.should == "2, 3"end
deflatitude return2
end
deflongitude
return3
end
object
_with_objectdefgeolocateloc =Zoogle.graveyard_locator(self.graveyard)
"#{loc.latitude}, #{loc.longitude}"
end
Stubs in the wild
-
7/27/2019 Testing With Rspec Slides
82/102
St bs i t iapp/mailers/zombie_mailer.rb
classZombieMailer < ActionMailer::Base
defwelcome(zombie)
mail(from: '[email protected]',to: zombie.email,
subject: 'Welcome Zombie!')
end
end
spec/mailers/zombie_mailer_spec.rb
subject { ZombieMailer.welcome(zombie) }
its(:from) { should include('[email protected]') }
its(:to) { should include(zombie.email) }
its(:subject) { should =='Welcome Zombie!' }
end
end
let(:zombie) { Zombie.create(email: '[email protected]') }
Not good, calling ActiveRecorddescribe ZombieMailerdo
context '#welcome'do
Stubs in the wild
mailto:[email protected]:[email protected]:[email protected]:[email protected] -
7/27/2019 Testing With Rspec Slides
83/102
t i t i
Lets create a fake object
let(:zombie) { stub(email: '[email protected]') }
let(:zombie) { Zombie.create(email: '[email protected]') }
Stubs in the wild
mailto:[email protected]:[email protected] -
7/27/2019 Testing With Rspec Slides
84/102
i iapp/mailers/zombie_mailer.rb
classZombieMailer < ActionMailer::Base
defwelcome(zombie)
mail(from: '[email protected]',to: zombie.email,
subject: 'Welcome Zombie!')
end
end
spec/mailers/zombie_mailer_spec.rb
describe ZombieMailerdocontext '#welcome'do
subject { ZombieMailer.welcome(zombie) }
its(:from) { should include('[email protected]') }
its(:to) { should include(zombie.email) }
its(:subject) { should =='Welcome Zombie!' }
end
end
let(:zombie) { stub(email: '[email protected]') }
More stub options
mailto:[email protected]:[email protected]:[email protected]:[email protected]:[email protected]:[email protected] -
7/27/2019 Testing With Rspec Slides
85/102
p i
target.should_receive(:function).once
.twice.exactly(3).times
.at_least(2).times
.at_most(3).times
.any_number_of_times
target.should_receive(:function).with(no_args())
.with(any_args())
.with("B", anything())
.with(3, kind_of(Numeric))
.with(/zombie ash/)
C t M t h
-
7/27/2019 Testing With Rspec Slides
86/102
Custom Matchers L EV EL 6
Refactoring
-
7/27/2019 Testing With Rspec Slides
87/102
i g
spec/models/zombie_spec.rb
classZombie < ActiveRecord::Base
validates :name, presence:trueend
app/models/zombie.rb
like include, but for hashes
we might do this a lot!describe Zombiedo
it 'validates presence of name'dozombie =Zombie.new(name:nil)zombie.valid?
zombie.errors.should have_key(:name) end
end
Refactoring
-
7/27/2019 Testing With Rspec Slides
88/102
g
so lets create this custom matcher
classZombie < ActiveRecord::Base
validates :name, presence:trueend
app/models/zombie.rb
spec/models/zombie_spec.rb
zombie.should validate_presence_of_name end
end
describe Zombiedo
it 'validates presence of name'dozombie =Zombie.new(name:nil)
Custom matchers
-
7/27/2019 Testing With Rspec Slides
89/102
classMatcher defmatches?(model)
model.valid?
model.errors.has_key?(:name)
end
end
moduleValidatePresenceOfName
spec/support/validate_presence_of_name.rb
defvalidate_presence_of_name
Matcher.new
end
same as our example
RSpec.configure do |config|
config.includeValidatePresenceOfName, type: :model
end
matcher available only to model specs
end
Custom matchers
-
7/27/2019 Testing With Rspec Slides
90/102
Zombie
validates presence of name
Finished in 0.02884 seconds1 example, 0 failures
rspec spec/models/zombie_spec.rb$
spec/models/zombie_spec.rb
zombie.should validate_presence_of_name end
end
describe Zombiedo
it 'validates presence of name'dozombie =Zombie.new(name:nil)
good, but we cant use this matcher for anything else, yet...
Modular matchers
-
7/27/2019 Testing With Rspec Slides
91/102
end
end
describe Zombiedo
it 'validates presence of name'dozombie =Zombie.new(name:nil)zombie.should validate_presence_of(:name)
Zombie
validates presence of name
Failures:
1) Zombie has errors on nameFailure/Error: zombie.should validate_presence_of(:name)
NoMethodError:
undefined method `validate_presence_of' for #
# ./spec/models/zombie.rb:5:in `block (2 levels) in '
1 example, 1 failure
it doesnt accept a parameter
rspec spec/models/zombie_spec.rb$
spec/models/zombie_spec.rb
Modular matchers
-
7/27/2019 Testing With Rspec Slides
92/102
spec/support/validate_presence_of_name.rb
moduleValidatePresenceOfName
classMatcher
end
model.errors.has_key?( ):name
defmatches?(model)
model.valid?
end
end
defvalidate_presence_of_name
Matcher.new
end
time to make this reusable!
Modular matchers
-
7/27/2019 Testing With Rspec Slides
93/102
end
end
end
end
Matcher.new(attribute)(attribute)defvalidate_presence_of
)@attributemodel.errors.has_key?(
defmatches?(model)
model.valid?
definitialize(attribute)
@attribute= attribute
end
classMatcher
moduleValidatePresenceOf
spec/support/validate_presence_of.rb
1) accept an attribute
2) store the attribute
3) use your attribute
now lets see if it works as we expect
Modular matchers
-
7/27/2019 Testing With Rspec Slides
94/102
spec/models/zombie_spec.rb
Zombie
validates presence of name
Finished in 0.02884 seconds1 example, 0 failures
rspec spec/models/zombie_spec.rb$
zombie.should validate_presence_of(:name) end
end
describe Zombiedo
it 'validates presence of name'dozombie =Zombie.new(name:nil)
it does, were green!
Matcher failures
-
7/27/2019 Testing With Rspec Slides
95/102
classZombie < ActiveRecord::Base
# validates :name, presence: true
end
spec/models/zombie_spec.rb
app/models/zombie.rb
zombie.should validate_presence_of(:name) end
end
describe Zombiedo
it 'validates presence of name'dozombie =Zombie.new(name:nil)
But what happens if the validation fails?
Matcher failures
-
7/27/2019 Testing With Rspec Slides
96/102
Zombie
validates presence of name
Failures:
1) Zombie validates presence of nameFailure/Error: zombie.should validate_presence_of(:name)
NoMethodError: undefined method 'failure_message'
Finished in 0.02884 seconds
1 example, 1 failures
asked your matcher for a failure message
spec/models/zombie_spec.rb
rspec spec/models/zombie_spec.rb$
zombie.should validate_presence_of(:name) end
end
describe Zombiedo
it 'validates presence of name'dozombie =Zombie.new(name:nil)
we havent defined one yet!
Matcher failures
-
7/27/2019 Testing With Rspec Slides
97/102
defmatches?(model)
@model=model
@model.valid?
@model.errors.has_key?(@attribute)
end
saved for use in messages
spec/support/validate_presence_of.rb
end
end
moduleValidatePresenceOf
classMatcher
deffailure_message
"#{@model.class} failed to validate :#{@attribute} presence."
end
defnegative_failure_message
"#{@model.class} validated :#{@attribute} presence." end
Matcher failures
-
7/27/2019 Testing With Rspec Slides
98/102
Zombie
validates presence of name
Failures:
1) Zombie validates presence of name
Failure/Error: zombie.should validate_presence_of(:name)
Expected Zombie to error on :name presence.
Finished in 0.02884 seconds
1 example, 1 failures
rspec spec/models/zombie_spec.rb
message from your matcher
$
Chained methods
-
7/27/2019 Testing With Rspec Slides
99/102
classZombie < ActiveRecord::Base
validates :name, presence: { message:'been eaten' }end
describe Zombiedo
it 'validates presence of name'dozombie =Zombie.new(name:nil)
end
end
zombie.should validate_presence_of(:name).
with_message('been eaten')
spec/models/zombie_spec.rb
app/models/zombie.rb
this error message should be returned on failure
Chained methods
-
7/27/2019 Testing With Rspec Slides
100/102
errors [email protected][@attribute]
errors.any? { |error| error ==@message }
@message="can't be blank"
definitialize(attribute) @attribute= attribute
end
classMatcher
defwith_message(message)
@message= message
self
end
defmatches?(model)
@model=model
@model.valid?
end
default failure message
collect errors and find a match
override failure message & return self
spec/support/validate_presence_of.rb
end
Chained methods
-
7/27/2019 Testing With Rspec Slides
101/102
describe Zombiedo
it 'validates presence of name'dozombie =Zombie.new(name:nil)
end
end
zombie.should validate_presence_of(:name).
with_message('been eaten')
spec/models/zombie_spec.rb
Zombie
validates presence of name
Finished in 0.02884 seconds
1 example, 0 failures
rspec spec/models/zombie_spec.rb$
Chained methods
-
7/27/2019 Testing With Rspec Slides
102/102
it { should validate_presence_of(:name).with_message('oh noes') }
it { should ensure_inclusion_of(:age).in_range(18..25) }it { should have_many(:weapons).dependent(:destroy) }
it { should have_many(:weapons).class_name(OneHandedWeapon) }
it 'has many Tweets' do
should have_many(:tweets).
dependent(:destroy).
class_name(Tweet)
end
multiple line chaining
single line chaining