Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to expect a message to be send to any instance of a class (twice total) with certain arguments in Rails?

When refunding an order we need to deactivate the serials attached with that order. In the spec the order has two serials, ergo I must expect two calls to the API that takes care of deactivating the serials.

I've tried:

expect_any_instance_of(Gateway).to receive(:deactivate_dongle).once.with(serial_number: '67890')
expect_any_instance_of(Gateway).to receive(:deactivate_dongle).once.with(serial_number: '12345')

Which gives me:

The message 'deactivate_dongle' was received by #<Gateway:70179265658480 @connection=#<Faraday::Connection:0x007fa7c4667b28>> but has already been received by #<Gateway:0x007fa7c6858160>

Same for:

expect_any_instance_of(Gateway).to receive(:deactivate_dongle).with(serial_number: '12345')
expect_any_instance_of(Gateway).to receive(:deactivate_dongle).with(serial_number: '67890')

How can I achieve that?

like image 871
Flip Avatar asked Apr 13 '17 15:04

Flip


2 Answers

In case you can change your implementation to make your expectations against a known instance of Gateway (in order to avoid expect_any_instance_of) you could use rspec's ordered method to add constraints on receiving messages.

I fear in your case you should try to add a spec where there is only one of those serials involved so you can expect it properly. e.g.

expect_any_instance_of(Gateway).to receive(:deactivate_dongle).once.with(serial_number: '67890')

And then have a spec with n serials and just expect deactivate_dongle being called n times.

like image 130
basiszwo Avatar answered Oct 03 '22 12:10

basiszwo


I think you need a Null Object:

Use the as_null_object method to ignore any messages that aren't explicitly set as stubs or message expectations.

allow_any_instance_of(JarvisGateway).to receive(:deactivate_dongle).as_null_object


Here's another solution I thought of before I found the above solution. Blocks offer more flexibility in checking args than the built-in matchers. See Use a block to verify arguments.

allow_any_instance_of(JarvisGateway).to receive(:deactivate_dongle) do |args|
  expect(['67890', '12345']).to include args[:serial_number]
end

expect_any_instance_of(JarvisGateway).to receive(:deactivate_dongle).twice


Not sure if allow_any_instance_of supports blocks this way.
If it doesn't then you can do it by one or more of the following:

  1. Create an instance of JarvisGateway and check the messages sent to it instead of allow_any_instance_of.
  2. Use has_received instead of to receive. See Spies.


EDIT: Actually expect_any_instance_of(JarvisGateway).to receive(:deactivate_dongle).twice isn't so good because it does not check to see that each serial number was called once.

like image 43
B Seven Avatar answered Oct 03 '22 12:10

B Seven