1

我正在尝试编写一个测试来断言所有定义的操作都在成功运行时被调用。我在列表中定义了给定进程的操作,并从容器中解析它们,如下所示:

class ProcessController
  def call(input)
    operations.each { |o| container[o].(input) }
  end

  def operations
    ['operation1', 'operation2']
  end

  def container
    My::Container # This is a Dry::Web::Container
  end
end

然后我测试如下:

RSpec.describe ProcessController do
  let(:container) { My::Container } 

  it 'executes all operations' do
    subject.operations.each do |op|
      expect(container[op]).to receive(:call).and_call_original
    end

    expect(subject.(input)).to be_success
  end
end

这会失败,因为container[operation_name]从内部调用ProcessController和从测试内部调用会产生不同的操作实例。我可以通过比较对象 ID 来验证它。除此之外,我知道代码工作正常并且所有操作都被调用。

容器配置为自动注册这些操作,并在测试开始运行之前完成。

如何使解析相同的键返回相同的项目?

4

1 回答 1

0

TL;DR - https://dry-rb.org/gems/dry-system/test-mode/


嗨,要获得您要求的行为,您需要在使用memoize容器注册项目时使用该选项。

请注意,Dry::Web::Container继承Dry::System::Container,其中包括Dry::Container::Mixin,所以虽然下面的例子是使用dry-container,它仍然适用:

require 'bundler/inline'

gemfile(true) do
  source 'https://rubygems.org'

  gem 'dry-container'
end

class MyItem; end

class MyContainer
  extend Dry::Container::Mixin

  register(:item) { MyItem.new }
  register(:memoized_item, memoize: true) { MyItem.new }
end

MyContainer[:item].object_id
# => 47171345299860
MyContainer[:item].object_id
# => 47171345290240

MyContainer[:memoized_item].object_id
# => 47171345277260
MyContainer[:memoized_item].object_id
# => 47171345277260

但是,要从干网执行此操作,您需要记住在同一路径下自动注册的所有对象,或者将# auto_register: false魔术注释添加到定义依赖项的文件顶部并手动启动它们

记忆可能会导致并发问题,具体取决于您使用的应用服务器以及您的对象是否在请求生命周期内发生了变异,因此干容器的设计默认不记忆。

另一个可以说是更好的选择是使用stubs

# Extending above code
require 'dry/container/stub'
MyContainer.enable_stubs!
MyContainer.stub(:item, 'Some string')

MyContainer[:item]
# => "Some string"

边注:

dry-system提供了一个注入器,因此您无需在对象中手动调用容器,因此您的流程控制器将变为:

class ProcessController
  include My::Importer['operation1', 'operation2']

  def call(input)
    [operation1, operation2].each do |operation|
      operation.(input)
    end
  end
end
于 2019-05-17T07:05:43.993 回答