Ответ 1
Для этого существует новый синтаксис:
expect_any_instance_of(Model).to receive(:save).at_least(:once)
У меня есть контроллер импорта в рельсах, который импортирует несколько csv файлов с несколькими записями в мою базу данных. Я бы хотел проверить в RSpec, если записи фактически сохранены с помощью RSpec:
<Model>.any_instance.should_receive(:save).at_least(:once)
Однако я получаю сообщение об ошибке:
The message 'save' was received by <model instance> but has already been received by <another model instance>
Надуманный пример контроллера:
rows = CSV.parse(uploaded_file.tempfile, col_sep: "|")
ActiveRecord::Base.transaction do
rows.each do |row|
mutation = Mutation.new
row.each_with_index do |value, index|
Mutation.send("#{attribute_order[index]}=", value)
end
mutation.save
end
Можно ли проверить это с помощью RSpec или есть ли способ обхода?
Для этого существует новый синтаксис:
expect_any_instance_of(Model).to receive(:save).at_least(:once)
Здесь лучший ответ, который позволяет избежать переопределения нового метода:
save_count = 0
<Model>.any_instance.stub(:save) do |arg|
# The evaluation context is the rspec group instance,
# arg are the arguments to the function. I can't see a
# way to get the actual <Model> instance :(
save_count+=1
end
.... run the test here ...
save_count.should > 0
Кажется, что метод stub может быть присоединен к любому экземпляру без ограничения, и блок do может сделать счет, который вы можете проверить, чтобы утверждать, что он был вызван правильным числом раз.
Обновление - для новой версии rspec требуется этот синтаксис:
save_count = 0
allow_any_instance_of(Model).to receive(:save) do |arg|
# The evaluation context is the rspec group instance,
# arg are the arguments to the function. I can't see a
# way to get the actual <Model> instance :(
save_count+=1
end
.... run the test here ...
save_count.should > 0
Мне, наконец, удалось сделать тест, который работает для меня:
mutation = FactoryGirl.build(:mutation)
Mutation.stub(:new).and_return(mutation)
mutation.should_receive(:save).at_least(:once)
Метод stub возвращает один экземпляр, который несколько раз получает метод сохранения. Поскольку это единственный экземпляр, я могу отказаться от метода any_instance
и обычно использовать метод at_least
.
Закрой как это
User.stub(:save) # Could be any class method in any class
User.any_instance.stub(:save) { |*args| User.save(*args) }
Затем ожидаем так:
# User.any_instance.should_receive(:save).at_least(:once)
User.should_receive(:save).at_least(:once)
Это упрощение this gist, чтобы использовать any_instance
, так как вам не требуется прокси-сервер для исходного метода. Обратитесь к этой теме для других целей.
Это пример Rob с помощью RSpec 3.3, который больше не поддерживает Foo.any_instance
. Я нашел это полезным, когда в цикле создания объектов
# code (simplified version)
array_of_hashes.each { |hash| Model.new(hash).write! }
# spec
it "calls write! for each instance of Model" do
call_count = 0
allow_any_instance_of(Model).to receive(:write!) { call_count += 1 }
response.process # run the test
expect(call_count).to eq(2)
end
Мое дело было немного другое, но я оказался в этом вопросе, чтобы понять, что и здесь. В моем случае я хотел заглушить любой экземпляр данного класса. Я получил ту же ошибку, когда использовал expect_any_instance_of(Model).to
. Когда я изменил его на allow_any_instance_of(Model).to
, моя проблема была решена.
Ознакомьтесь с документацией для получения дополнительной информации: https://github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class