Как сказать "any_instance" "should_receive" любое количество раз в RSpec
У меня есть контроллер импорта в rails, который импортирует несколько файлов 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 или есть обходной путь?
6 ответов
Для этого есть новый синтаксис:
expect_any_instance_of(Model).to receive(:save).at_least(:once)
Вот лучший ответ, который позволяет избежать необходимости переопределять метод: new:
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
Кажется, что метод-заглушка может быть присоединен к любому экземпляру без ограничения, а блок 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
Это пример Роба, использующего 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
Мне наконец удалось сделать тест, который работает для меня:
mutation = FactoryGirl.build(:mutation)
Mutation.stub(:new).and_return(mutation)
mutation.should_receive(:save).at_least(:once)
Метод заглушки возвращает один единственный экземпляр, который получает метод сохранения несколько раз. Поскольку это единственный экземпляр, я могу отбросить 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)
Это упрощение этой сути, чтобы использовать any_instance
, так как вам не нужно использовать прокси для исходного метода. Обратитесь к этой сути для других целей.
Мой случай был немного другим, но я остановился на этом вопросе и решил оставить здесь свой ответ. В моем случае я хотел заглушить любой экземпляр данного класса. Я получил ту же ошибку, когда я использовал expect_any_instance_of(Model).to
, Когда я изменил это на allow_any_instance_of(Model).to
Моя проблема была решена.
Посмотрите документацию для получения дополнительной информации: https://github.com/rspec/rspec-mocks
Вы можете попытаться подсчитать количество new
на классе. На самом деле это не тесты, количествоsave
s но может быть достаточно
expect(Mutation).to receive(:new).at_least(:once)
Если есть единственное ожидание, сколько раз он был сохранен. Тогда вы, вероятно, захотите использоватьspy()
вместо полностью функционирующего завода, как в Harm de Wit
собственный ответ
allow(Mutation).to receive(:new).and_return(spy)
...
expect(Mutation.new).to have_received(:save).at_least(:once)