Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RSpec: how do I write a test that expects certain output but doesn't care about the method?

Tags:

tdd

ruby

rspec

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?

like image 674
Andy Avatar asked Jun 16 '11 13:06

Andy


People also ask

How do I mock a method in RSpec?

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.

What is double in RSpec?

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.

How does a mock work in Ruby?

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.


1 Answers

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.

like image 159
Finglas Avatar answered Oct 08 '22 00:10

Finglas