14. DUMMY OBJECT
A placeholder that is passed to the SUT and never used
let(:side_a) { 1 }
let(:side_b) { 2 }
let(:dummy) { Object.new }
subject { HighSchoolTrig.hypotenuse(a, b, dummy) }
it { should eq 2.236 }
15. FAKE OBJECT
An object which replaces the real DOC with 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 }
16. MOCKS, STUBS & SPIES
An example:
class User < ActiveRecord::Base
before_create :enqueue_welcome_message
def enqueue_welcome_message
queue = Application.config.email_queue
raise(“Failed to queue”) unless queue.push(email, “Welcome”)
end
end
17. NO DOUBLES
let(:email) { “tom@crui.se” }
subject { User.create(email: email) }
it { should be_persisted }
its(:username) { should eq email }
18. MOCK OBJECT
An object which replaces the real DOC that can verify indirect
output from the SUT with expectations
let(:mock_queue) { double() }
let(:email) { “tom@crui.se” }
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 }
19. TEST STUB
An object which replaces the real DOC to control indirect
input to the SUT
let(:stub_queue) { double(push: true) }
let(:email) { “tom@crui.se” }
before do
Application.config.email_queue = stub_queue
end
subject { User.create(email: email) }
it { should be_persisted }
its(:username) { should eq email }
20. TEST SPY
A more capableTest Stub allowing verification of indirect output
from the SUT
let(:spy_queue) { double(push: true) }
let(:email) { “tom@crui.se” }
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
22. class Buddy
def good_friend?; on_tap.craft?; end
def on_tap
Fridge.cold_one
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
23. DEPENDENCY LOOKUP
class Buddy
def good_friend?; on_tap.craft?; end
def 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
24. class Buddy
attr_accessor :fridge
def good_friend?; on_tap.craft?; end
def 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
27. TEST-SPECIFIC SUBCLASSES
class Buddy
attr_reader :supermarket
def make_breakfast(request=“Steak & eggs”)
ingredients = supermarket.find(request)
prepare(ingredients)
end
end
class TestBuddy < Buddy
attr_writer :supermarket
end
28. TEST HOOKS
class Buddy
if ENV != “TEST”
attr_reader :supermarket
else
attr_accessor :supermarket
end
def make_breakfast(request=“Steak & eggs”)
ingredients = supermarket.find(request)
prepare(ingredients)
end
end
30. “MOCKIST”TDD / BDD
• Uses mocks for all DOCs
• Likes the test writing process to inform design decisions
• Tests in strict isolation
31. CLASSICTDD
• Uses test doubles only for awkward DOCs, favoring “real”
objects
• Minimizes coupling between tests and implementation
• Tests small clusters of components, not isolated units
32. CLASSICTDDERS CONSIDER
USING ATEST DOUBLE IF:
• The behavior of the DOC cannot be changed/observed
• Use of the DOC could cause unwanted side-effects
• The DOC is too slow
• The DOC doesn’t exist yet
33. OVERUSE CAN LEADTO:
• Over specified tests of the SUT’s process, not its result
• Fragile tests that break when implementation changes
• Untested integration
• Less time on Hacker News while your build runs
34. MORE
xUnit Test Patterns: xunitpatterns.com
Mocks aren’t Stubs by 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