22

I need to verify that any instance of my class receives a certain method, but I don't care if many instances receive it (they're supposed to).

I tried like this:

expect_any_instance_of(MyClass).to receive(:my_method).at_least(:once)

But apparently, it only allows for a single instance to receive the method multiple times, but not for different instances.

Is there a way to achieve that?

messanjah
  • 8,977
  • 4
  • 27
  • 40
Mat
  • 952
  • 2
  • 11
  • 28

2 Answers2

29

If you need to live with the code smell, this rspec-mocks Github issue suggests a solution along these lines:

receive_count = 0
allow_any_instance_of(MyClass).to receive(:my_method) { receive_count += 1 }

# Code to test here.

expect(receive_count).to be > 0
Gabriel Deal
  • 935
  • 1
  • 8
  • 19
  • 1
    This is exactly what I would need, but rspec (3.5) still complains about being received by different objects... The message 'my_method' was received by # but has already been received by # (RSpec::Mocks::MockExpectationError) – Mathieu J. Jul 26 '18 at 05:00
  • 12
    a few years late, but just in case anyone needs help here. you're probably still using `expect_any_instance_of` which will throw that error when a second call is made. you have to change it to `allow_any_instance_of` which has no expectations on number of calls made – Mr. T Jul 03 '20 at 20:48
  • 1
    TL;DR: use `allow_any…` instead of `expect_any…` – Edward Anderson Jan 09 '23 at 16:38
11

This is a known issue in rspec-mocks. From the v3.4 documentation on Any instance:

The rspec-mocks API is designed for individual object instances, but this feature operates on entire classes of objects. As a result there are some semantically confusing edge cases. For example, in expect_any_instance_of(Widget).to receive(:name).twice it isn't clear whether a specific instance is expected to receive name twice, or if two receives total are expected. (It's the former.)

Furthermore

Using this feature is often a design smell. It may be that your test is trying to do too much or that the object under test is too complex.

Do you have any way to refactor your test or app code to avoid the "confusing edge case"? Perhaps by constructing a test double and expecting it to receive messages?

messanjah
  • 8,977
  • 4
  • 27
  • 40
  • Maybe by telling my class that any new object should be a specific one that I create in my test? Is this common practice? – Mat Feb 11 '16 at 08:24
  • That sounds plausible. I can't speak to the popularity, but give it a shot! – messanjah Feb 11 '16 at 15:49
  • 2
    This doesn't always mean code smell. Some probably have changed from `MyClass.my_method` to `MyClass.new.my_method` to make it more OO and less code smell. – lulalala May 08 '18 at 06:23