How to ignore extra messages with RSpec should_receive?

3.9k views Asked by At

Spec:

before do
  Logger.should_receive( :write ).with 'Log message 1'    
end

it 'works' do
  get '/'
end

Sinatra App:

get '/'
  Logger.write( 'Log message 1' )
  Logger.write( 'Log message 2' )
end

This spec fails because of 'Log message 2'. How to tell RSpec to ignore any other messages, and only test for the expected message?

2

There are 2 answers

2
Ash Wilson On

You need to stub the method that will be receiving the message before the message expectation.

# RSpec < 3
Logger.stub(write: nil)

The stub method is deprecated in RSpec 3, instead use one of the following

# RSpec >= 3
allow(Logger).to receive(:write).and_return(nil)  # Most appropriate in this case
allow(Logger).to receive(:write) { nil }  # Prefer block when returning something of a dynamic nature, e.g. a calculation
allow(Logger).to receive_messages(write: nil)  # Prefer hash when stubbing multiple methods at once, e.g. receive_messages(info: nil, debug: nil)

A method stub is an instruction to an object (real or test double) to return a known value in response to a message.

In this case, tell the Logger object to return the value nil when it receives the write message (the first time).

So your before block should look like this

before do
  Logger.stub(write: nil)
  Logger.should_receive(:write).with('Log message 1')
end
3
gondalez On

This question is a little old, but having found my way here I thought I would answer it assuming rspec-mocks v3.

Stubbing the object beforehand then asserting with have_received works well when you care that the object receives a certain message, not that it only receives the message.

The subtle difference being between receive(:...) and have_received(:...)

To follow on from the original question, and assuming it was rewritten in rspec-mocks v3, my solution would be:

allow(Logger).to receive(:write)
get '/'
expect(Logger).to have_received(:write).with('Log message 1')

Note it is important to place the assertion at the end as it inspects the stub state when called, not on completion as is customary.