testing with rspec slides

Upload: dei-bit

Post on 14-Apr-2018

251 views

Category:

Documents


0 download

TRANSCRIPT

  • 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