I'm trying to get my head around test-driven design, specifically RSpec. But I'm having trouble with some of the examples from The RSpec Book.
In the book, we test for output on $STDOUT like this:
output = double('output')
game = Game.new
output.should_receive(:puts).with('Welcome to Codebreaker!')
game.start()
Well, that works after a fashion. But why on earth should I care if the Game object uses the puts() method? If I change it to print(), should it really break the test? And, more importantly, isn't this against the one of the principals of TDD - that I should be testing what the method does (the design) rather than how it does it (the implementation)?
Is there some way I can write a test that just tests what ends up on $STDOUT, without looking at what method puts it there?
Mocking with RSpec is done with the rspec-mocks gem. If you have rspec as a dependency in your Gemfile , you already have rspec-mocks available.
RSpec features doubles that can be used as 'stand-ins' to mock an object that's being used by another object. Doubles are useful when testing the behaviour and interaction between objects when we don't want to call the real objects - something that can take time and often has dependencies we're not concerned with.
Mocks are a handy tool for writing tests in Ruby. You can use them to fake an object and verify that the correct methods were called against it. Perfect for testing a method that integrates closely with another class or module.
Create a display class with the ability to write the status out.
You production code will make use of this display object so you are free to change how you write to STDOUT. There will be just one place for this logic while your tests rely on the abstraction.
For example:
output = stub('output')
game = Game.new(output)
output.should_receive(:display).with('Welcome to Codebreaker!')
game.start()
While your production code will have something such as
class Output
def display(message)
# puts or whatever internally used here. You only need to change this here.
end
end
I'd make this test pass by doing the following:
def start
@output.display('Welcome to Codebreaker!')
end
Here the production code doesn't care how the output is displayed. It could be any form of display now the abstraction is in place.
All of the above theory is language agnostic, and works a treat. You still mock out things you don't own such as third party code, but you are still testing you are performing the job at hand via your abstraction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With