Dry::Web::Container, выдающий различные объекты с несколькими вызовами для разрешения
Я пытаюсь написать тест, чтобы подтвердить, что все определенные операции вызываются при успешном запуске. У меня есть операции для данного процесса, определенные в списке, и я разрешаю их из контейнера, например так:
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
и изнутри теста выдают различные экземпляры операций. Я могу это проверить, сравнив идентификаторы объектов. Кроме этого, я знаю, что код работает правильно и все операции вызываются.
Контейнер сконфигурирован для автоматической регистрации этих операций и был завершен до начала теста.
Как сделать так, чтобы при разрешении одного и того же ключа возвращался один и тот же элемент?
1 ответ
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
Однако, чтобы сделать это из Dry-Web, вам нужно либо запомнить все объекты, автоматически зарегистрированные по одному и тому же пути, либо добавить # auto_register: false
Волшебный комментарий к началу файлов, которые определяют зависимости и загружают их вручную.
Создание мемоизаций может вызвать проблемы параллелизма в зависимости от того, какой сервер приложений вы используете, и от того, мутируют ли ваши объекты в течение жизненного цикла запроса, поэтому конструкция сухого контейнера не запоминается по умолчанию.
Другой, возможно, лучший вариант, это использовать заглушки:
# 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