Suppose I have a class that is supposed to call a service multiple times with different arguments
class MyExecutor
def perform
targets.each do |target|
MyService.new(target).call
end
end
end
class MyService
def initialize(target)
@target = target
end
def call
@target.do_something
end
end
Assume I want to write a test on MyExecutor
, generating data so that I have at least 2 targets, and I want to test that the service MyService
is called appropriately on all targets.
When I had only one target, I could use expect_any_instance_of().to receive(:call)
but then I was not really testing the instanciation with appropriate params, and then this syntax is deprecated (cf comment here)
describe MyExecutor
context 'with one target'
it 'calls the MyService appropriately'
expect_any_instance_of(MyService).to receive(:call)
MyExecutor.perform
end
end
end
Suppose I have multiple targets, how can I test that the MyService is instanciated twice, once with each relevant target, and that on each of those instanciated services, the call
method is called appropriately What is the proper non-deprecated way to test this ?
Implicit question : (is this the right way to approach the problem ?)
In Rspec 3.8 syntax:
describe MyExecutor do
subject(:executor) { described_class.new }
describe '#perform' do
subject(:perform) { executor.perform }
let(:target1) { instance_double('target' }
let(:target2) { instance_double('target' }
let(:service1) { instance_double(MyService, call: true) }
let(:service2) { instance_double(MyService, call: true) }
before do
allow(MyExecutor).to receive(:targets).and_return([target1, target2])
allow(MyService).to receive(:new).with(target1).and_return(service1)
allow(MyService).to receive(:new).with(target2).and_return(service2)
perform
end
it 'instantiates MyService once for each target' do
expect(MyService).to have_received(:new).with(target1).ordered
expect(MyService).to have_received(:new).with(target2).ordered
end
it 'calls MyService once for each target' do
expect(service1).to have_received(:call)
expect(service2).to have_received(:call)
end
end
end
Note that using .ordered
allows you to specify the exact order of operations.
Note that doubling MyService .with
a specific parameter allows you to control the return value for that specific parameter.