astgtciv astgtciv - 4 months ago 20x
Ruby Question

How to make rspec-mocks' expect to receive.with fail eagerly


expect(target).to receive(:message).with(arg_matcher)
will only show an error at the end of the test if the target is invoked with parameters not matching the arg matcher passed to
. Is there a way to force it to fail eagerly - i.e., as soon as the target is invoked with non-matching params? RR works in this way.

The problem I am facing is that when I set up this mock with an arg_matcher as above, the test starts failing because the target is called with different params, but then another assertion fails before the end of the test, so I only see the error from this assertion, not from the missing mock (which would have shown me the difference between the expected params and the actually invoked ones).

Using rspec-mocks 3.3.2.


I don't know of a way to make receive fail eagerly. However, have_received fails eagerly, so if it is the first of several expectations it will be the one that fails the test and that RSpec reports.

class Foo

describe "RSpec" do
  it "reports a non-mock expectation failure before a mock expectation failure" do
    expect(Foo).to receive(:bar).with(1)
    expect(true).to be_falsy # RSpec reports this failure 2

  it "reports a spy expectation failure when you'd expect it to be reported" do
    allow(Foo).to receive(:bar) # Spy on Foo 2
    expect(Foo).to have_received(:bar).with(1) # RSpec reports this failure
    expect(true).to be_falsy


See the documentation of RSpec spies for details.

In addition to being a solution to your problem, spies also provide the more logical arrange-act-assert sequence. Whether you prefer this solution or Adarsh's aggregate_failures solution depends partly on how much it bugs you to have to set up the spy with allow as well as writing the expectation, and also whether the second expectation (the one that was masking the mock expectation failure) has any meaning if the mock expectation fails or is just noise in that case.