Как игнорировать лишние сообщения с помощью RSpec should_receive?

Спецификация:

before do
  Logger.should_receive( :write ).with 'Log message 1'    
end

it 'works' do
  get '/'
end

Приложение Синатра:

get '/'
  Logger.write( 'Log message 1' )
  Logger.write( 'Log message 2' )
end

Эта спецификация не работает из-за «Сообщения журнала 2». Как указать RSpec игнорировать любые другие сообщения и проверять только ожидаемое сообщение?


person B Seven    schedule 10.12.2013    source источник
comment
Logger.should_receive(:write).at_least(:once). Или вам нужно утверждать точное сообщение?   -  person Sergio Tulentsev    schedule 21.10.2014


Ответы (2)


Вам нужно заглушить метод, который будет получать сообщение до ожидания сообщения.

# RSpec < 3
Logger.stub(write: nil)

Метод stub устарел в RSpec 3, вместо этого используйте один из следующих

# RSpec >= 3
allow(Logger).to receive(:write).and_return(nil)  # Most appropriate in this case
allow(Logger).to receive(:write) { nil }  # Prefer block when returning something of a dynamic nature, e.g. a calculation
allow(Logger).to receive_messages(write: nil)  # Prefer hash when stubbing multiple methods at once, e.g. receive_messages(info: nil, debug: nil)

Заглушка метода — это инструкция для объекта (реального или тестового двойника) вернуть известное значение в ответ на сообщение.

В этом случае скажите объекту Logger вернуть значение nil, когда он получит сообщение write (в первый раз).

Итак, ваш блок before должен выглядеть так

before do
  Logger.stub(write: nil)
  Logger.should_receive(:write).with('Log message 1')
end
person Ash Wilson    schedule 10.12.2013
comment
Logger.stub(:write) работал. Почему это необходимо? - person B Seven; 11.12.2013
comment
stub позволяет экземпляру получать сообщение без фактического вызова метода. should_receive устанавливает ожидание, что означает, что спецификация должна завершиться ошибкой, если сообщение получено не так, как ожидалось, включая сопоставление аргументов. В этом случае вы хотите ожидать конкретный вызов и разрешить общий вызов, следовательно, вызывая оба метода. По сути, вызов stub говорит, и это нормально, если Logger вызывает write и с другими аргументами. - person Ash Wilson; 11.12.2013

Этот вопрос немного устарел, но, найдя здесь свой путь, я подумал, что отвечу на него, предполагая, что rspec-mocks v3.

Заглушка объекта заранее, а затем утверждение с помощью have_received работает хорошо, когда вам важно, чтобы объект получил определенное сообщение, а не чтобы он только получил сообщение.

Тонкая разница между receive(:...) и have_received(:...)

Чтобы продолжить исходный вопрос и предположить, что он был переписан в rspec-mocks v3, моим решением было бы:

allow(Logger).to receive(:write)
get '/'
expect(Logger).to have_received(:write).with('Log message 1')

Обратите внимание, что утверждение важно размещать в конце, так как оно проверяет состояние заглушки при вызове, а не при завершении, как это принято.

person gondalez    schedule 22.03.2018
comment
Интересно, как это сделать с have_received... или есть особая причина, по которой receive работает не так, как have_received? - person B Seven; 22.03.2018
comment
На мой взгляд, expect(Logger).to receive(:write).with('Log message 1') утверждает, что Logger получает только это единственное сообщение (или любые другие, которые вы можете добавить). Это тонкая и плохо документированная разница. Я постараюсь найти время, чтобы отправить PR rspec-mocks, чтобы обновить их readme/docs. - person gondalez; 23.03.2018
comment
Да, похоже, это текущее поведение. Может быть метод only, который делает это явным и может использоваться как с receive, так и с have_received. expect(Logger).to receive(:write).with('Log message 1').only - person B Seven; 23.03.2018