Rspec - 如何测试嵌套服务是否已正确实例化以及是否在此实例上调用了适当的方法

Rspec - how to test that a nested service is instanciated appropriately and the appropriate method is called on this instance

假设我有一个 class 应该使用不同的参数多次调用服务

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

假设我想在 MyExecutor 上编写一个测试,生成数据以便我至少有 2 个目标,并且我想测试服务 MyService 是否在所有目标上被正确调用。

当我只有一个目标时,我可以使用 expect_any_instance_of().to receive(:call) 但后来我并没有真正用适当的参数测试实例化,然后这个语法就被弃用了 (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

假设我有多个目标,我如何测试 MyService 被实例化两次,每个相关目标一次,并且在每个实例化服务上,call 方法是适当地调用测试这个的正确的非弃用方法是什么?

隐式问题:(这是解决问题的正确方法吗?)

我想我理解你的需求:

 describe MyExecutor do
   context 'with one target' do
     it 'calls the MyService appropriately' do
       target1 = double("TargetClass")
       target2 = double("TargetClass")
       allow(MyExecutor).to receive(:targets).and_return([target1, target2])

       service1 = double(MyService, call: nil)
       service2 = double(MyService, call: nil)
       expect(MyService).to receive(:new).with(target1).once.and_return(service1)
       expect(MyService).to receive(:new).with(target2).once.and_return(service2)

       MyExecutor.perform
     end
   end
 end

在 Rspec 3.8 语法中:

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

请注意,使用 .ordered 可以指定操作的确切顺序。

请注意,加倍 MyService .with 特定参数允许您控制该特定参数的 return 值。