Search code examples
rubyrspecrspec-mocks

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


rspec-mocks' 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 with. 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.


Solution

  • 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
      def self.bar(baz)
      end
    end
    
    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
        Foo.bar 2
      end
    
      it "reports a spy expectation failure when you'd expect it to be reported" do
        allow(Foo).to receive(:bar) # Spy on Foo
        Foo.bar 2
        expect(Foo).to have_received(:bar).with(1) # RSpec reports this failure
        expect(true).to be_falsy
      end
    
    end
    

    See the documentation of RSpec spies for details.

    This solution is probably best if

    • if you prefer your tests in the logical arrange-act-assert sequence like I do
    • if the spy expectation fails, you don't care about the other expectation
    • if you mind the extra typing of the allow that sets up the spy less than you mind the aggregate_failures block

    Regular mocks and Adarsh's solution are better

    • you want to see the results of both expectations regardless of whether either fails
    • if you mind the aggregate_failures block less than you mind the extra typing of the allow that sets up the spy