Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Safe Testing in Ruby

sporto
September 26, 2013

Safe Testing in Ruby

A look at safe doubles, stubs and mock with Rspec and Bogus

sporto

September 26, 2013
Tweet

More Decks by sporto

Other Decks in Programming

Transcript

  1. “Safe testing”
    @sebasporto
    Thursday, 26 September 13

    View Slide

  2. user
    let(:user) { User.new }
    it “tests something” do
    expect(user).to be_something
    end
    Thursday, 26 September 13

    View Slide

  3. Things get complicated
    user
    language
    location
    roles
    permissions
    Thursday, 26 September 13

    View Slide

  4. let(:location) { Location.new }
    ...
    let(:user) {
    User.new(location: location, language: language)
    }
    We can create things manually
    Or use factories
    let(:user) { build(:user) }
    Thursday, 26 September 13

    View Slide

  5. Problem 1:
    A whole universe to test
    one thing
    Thursday, 26 September 13

    View Slide

  6. it ‘increase the watch count’ {
    user.watch(video)
    expect(user.watch_count).to
    }
    Problem 2: Too much happening
    class User
    def watch(video)
    ...
    video.watched_by(self)
    end
    end
    class Video
    def watched_by(user)
    ...
    end
    end
    Thursday, 26 September 13

    View Slide

  7. SLOW!!!
    Thursday, 26 September 13

    View Slide

  8. Doubles! Stubs!
    Thursday, 26 September 13

    View Slide

  9. class User
    def watch(video)
    ...
    video.watched_by(self)
    end
    end
    Much faster!!
    No interaction
    with video
    anymore
    let(:video) { double.as_null_object }
    it {
    user.watch(video)
    expect(user.watch_count).to
    }
    Thursday, 26 September 13

    View Slide

  10. let(:video) { double.as_null_object }
    it {
    user.watch(video)
    expect(user.watch_count).to
    }
    But it may break
    class User
    def watch(video)
    ...
    video.watched_by(self)
    end
    end
    class Video
    def seen_by(user)
    ...
    end
    end
    Test still passes!
    Thursday, 26 September 13

    View Slide

  11. Not to worry.
    Integration test
    will save you!
    Thursday, 26 September 13

    View Slide

  12. Integration breaks
    Fix the code in B
    Unit test for B breaks
    Fix unit test for B
    Change code in A
    Thursday, 26 September 13

    View Slide

  13. Integration test coverage?
    Will they?
    Thursday, 26 September 13

    View Slide

  14. Your test
    Thursday, 26 September 13

    View Slide

  15. Your app
    Thursday, 26 September 13

    View Slide

  16. Can we get benefits
    without the drawbacks?
    Thursday, 26 September 13

    View Slide

  17. Safer testing
    Rspec fire
    Quacky
    Bogus
    ...
    Thursday, 26 September 13

    View Slide

  18. Bogus
    Safe Fakes
    Safe Stubbing
    Safe Mocks
    Thursday, 26 September 13

    View Slide

  19. Fakes
    Thursday, 26 September 13

    View Slide

  20. class User
    def watch(video)
    ...
    video.watched_by(self)
    end
    end
    Rspec doubles
    let(:video) { double.as_null_object }
    it {
    user.watch(video)
    ...
    1 example, 0 failures still passes
    class Video
    def seen_by(user)
    ...
    end
    end
    Thursday, 26 September 13

    View Slide

  21. class User
    def watch(video)
    ...
    video.watched_by(self)
    end
    end
    undefined method `watched_by'
    it breaks
    Bogus Fakes
    class Video
    def seen_by(person)
    ...
    end
    end
    fake(:video) { Video }
    it {
    user.watch(video)
    ...
    Thursday, 26 September 13

    View Slide

  22. wrong number of arguments (2 for 1)
    it breaks
    Fakes (args)
    class User
    def watch(video)
    ...
    video.watched_by(self, args)
    end
    end
    class Video
    def watched_by(user)
    ...
    end
    end
    fake(:video) { Video }
    it {
    user.watch(video)
    ...
    Thursday, 26 September 13

    View Slide

  23. Stubbing
    Thursday, 26 September 13

    View Slide

  24. Rspec Stubbing
    let(:video) { double.as_null_object }
    before do
    video
    .stub(:time)
    .and_return(14.minutes)
    end
    it {
    user.watch(video)
    expect(user.busy_for).to eq(14.minutes)
    1 example, 0 failures
    passes even if video.time
    doesn’t exist
    class User
    ...
    def busy_for
    current_video.time
    end
    ...
    end
    class Video
    def duration
    ...
    end
    Thursday, 26 September 13

    View Slide

  25. Bogus Stubbing
    fake(:video) { Video }
    before do
    stub(video)
    .time{ 14.minutes }
    end
    it {
    user.watch(video)
    expect(user.busy_for).to eq(14.minutes)
    }
    does not respond to time
    This is what we want
    class User
    ...
    def busy_for
    current_video.time
    end
    ...
    end
    class Video
    def duration
    ...
    end
    Thursday, 26 September 13

    View Slide

  26. Stubbing (args)
    wrong number of arguments (0 for 1)
    fake(:video) { Video }
    before do
    stub(video)
    .time(any_args){ 14.minutes }
    end
    it {
    user.watch(video)
    expect(user.busy_for).to eq(14.minutes)
    }
    class User
    ...
    def busy_for
    current_video.time
    end
    ...
    end
    class Video
    def time(args)
    ...
    end
    Thursday, 26 September 13

    View Slide

  27. Expectations
    (Mocking)
    Thursday, 26 September 13

    View Slide

  28. Rspec Mocking
    let(:video) { double.as_null_object }
    it {
    video.stub(:watched_by)
    user.watch(video)
    expect(video).to
    have_received(watched_by)
    }
    1 example, 0 failures
    class User
    ...
    def watch(video)
    ...
    video.watched_by(self)
    end
    ...
    class Video
    def seen_by(person)
    ...
    end
    end
    Thursday, 26 September 13

    View Slide

  29. Bogus Mocks
    let(:video) { double.as_null_object }
    it {
    mock(video).watched_by(any_args)
    user.watch(video)
    }
    does not respond to watched_by
    class User
    ...
    def watch(video)
    ...
    video.watched_by(self)
    end
    ...
    class Video
    def seen_by(person)
    ...
    end
    end
    Thursday, 26 September 13

    View Slide

  30. Bogus gem
    https://www.relishapp.com/bogus
    Thursday, 26 September 13

    View Slide

  31. Thanks
    @sebasporto
    Thursday, 26 September 13

    View Slide