Monitoring Specific Messages in RSpec Part 2

Check out the full Monitoring Messages in Rspec series: part 1, part 2, part 3

Last time it was demonstrated how it is possible to monitor only a desired message expectations. While that technique is useful for a vast majority of use cases, sometimes you need a bit more complexity.

Say for example, the desire is to verify the state of the provided parameter. As in this very contrived example (Update: 2013-07-28 Paired down example to only show test and not implementation):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
describe Factory do
  context 'making sure current stock is ready' do
    it 'only fixes broken widgets' do
      # Setup code

      states = []
      expect(mechanic).to receive(:fix) do |widget|
        states << widget.broken?
      end

      expect{ factory.perform_maintenance }.to change{states}.to [true]
    end
  end
end

This leverages the stub with substitute implementation provided by RSpec mocks. It can also be used with the allow syntax.

While the above version has it’s uses, it tends to hide some of the intent in the closure manipulation. A slightly more expressive method is just to add the state expectation in the block:

1
2
3
expect(mechanic).to receive(:fix) do |widget|
  expect(widget).to be_broken
end

This last technique is great for setting up generic message stubs which require that something with specific state is provided. By adding it to a generic allow, it ensures when the contract is broken anywhere in the code under test, the test will properly fail. (Update 2013-07-30: seems there is an issue with this technique when combined with and_call_original)