let(:drays) { Team.new “Tampa” } before { sox.play(drays) } ! it “should set standings” do expect(drays).to be_losing end ! after { standings.delete_all } setup
“Boston” } let(:drays) { Team.new “Tampa” } before { sox.play(drays) } ! it “should set standings” do expect(drays).to be_losing end ! after { standings.delete_all }
“Boston” } let(:drays) { Team.new “Tampa” } before { sox.play(drays) } ! it “should set standings” do expect(drays).to be_losing end ! after { standings.delete_all }
“Boston” } let(:drays) { Team.new “Tampa” } before { sox.play(drays) } ! it “should set standings” do expect(drays).to be_losing end ! after { standings.delete_all }
“Boston” } let(:drays) { Team.new “Tampa” } before { sox.play(drays) } ! it “should set standings” do expect(drays).to be_losing end ! after { standings.delete_all }
an alternate implementation of the same functionality class FakePiCalc def pi; 3.14159; end end ! let(:radius) { 2 } ! before { MyGeo.pi_calculator = FakePiCalc } ! subject { MyGeo.circumference(radius) } ! it { should eq 13 }
can verify indirect output from the SUT with expectations let(:mock_queue) { double() } let(:email) { “[email protected]” } ! before do Application.config.email_queue = mock_queue expect(mock_queue).to receive(:push).with(email, “Welcome”) end subject { User.create(email: email) } ! it { should be_persisted } its(:username) { should eq email }
control indirect input to the SUT let(:stub_queue) { double(push: true) } let(:email) { “[email protected]” } ! before do Application.config.email_queue = stub_queue end ! subject { User.create(email: email) } ! it { should be_persisted } its(:username) { should eq email }
indirect output from the SUT let(:spy_queue) { double(push: true) } let(:email) { “[email protected]” } ! before do Application.config.email_queue = spy_queue end ! subject { User.create(email: email) } ! it { should be_persisted } its(:username) { should eq email } it “should enqueue welcome message” do expect(spy_queue).to have_received(:push).with(email, “Ohai”) end
end end ! describe Buddy, “serving coors” do # TODO control indirect input to the SUT it “should not be a good friend” do expect(subject).not_to be_good_friend end
on_tap Fridge.cold_one end end ! describe Buddy, “serving coors” do let(:coors) { double(craft?: false) } ! before { Fridge.stubs(:cold_one) { coors } } it “should not be a good friend” do expect(subject).not_to be_good_friend end
on_tap @fridge.cold_one end end ! describe Buddy, “serving coors” do let(:coors) { double(craft?: false) } let(:stub_fridge) { double(cold_one: coors) } before { subject.fridge = stub_fridge } it “should not be a good friend” do expect(subject).not_to be_good_friend end DEPENDENCY INJECTION
! def make_breakfast(request=“Steak & eggs”) ingredients = supermarket.find(request) prepare(ingredients) end end ! class TestBuddy < Buddy attr_writer :supermarket end
SUT’s process, not its result • Fragile tests that break when implementation changes • Untested integration • Less time on Hacker News while your build runs
Martin Fowler: martinfowler.com/articles/mocksArentStubs.html ! A case against a case against mocking and stubbing by David Chelimsky: blog.davidchelimsky.net/2008/12/11/a-case-against-a-case-against-mocking-and-stubbing/ ! Timecop for testing time-dependent code: github.com/travisjeffery/timecop ! RSpec: rspec.info ! MiniTest: ruby-doc.org/stdlib ! Mocha: github.com/freerange/mocha