Как сказать "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на классе. На самом деле это не тесты, количествоsaves но может быть достаточно

    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)
Другие вопросы по тегам