RSpec: как проверить, был ли вызван метод?

112

При написании тестов RSpec я обнаружил, что пишу много кода, который выглядит следующим образом, чтобы гарантировать, что метод был вызван во время выполнения теста (в качестве аргумента, скажем так, я не могу действительно опросить состояние объекта после вызова, потому что действие, выполняемое методом, нелегко увидеть).

describe "#foo"
  it "should call 'bar' with appropriate arguments" do
    called_bar = false
    subject.stub(:bar).with("an argument I want") { called_bar = true }
    subject.foo
    expect(called_bar).to be_true
  end
end

Я хочу знать: есть ли более удобный синтаксис, чем этот? Не хватает ли мне какой-то забавной удивительности RSpec, которая сократила бы приведенный выше код до нескольких строк? should_receiveзвучит так, как будто он должен это делать, но при дальнейшем чтении кажется, что это не совсем то, что он делает.

Майки Хогарт
источник
3
Проверьте здесь: stackoverflow.com/questions/1328277/…
kddeisz
@Peter Alfvin OP запрашивал синтаксис should_receive, поэтому я подумал, что этот вопрос поможет.
kddeisz

Ответы:

141
it "should call 'bar' with appropriate arguments" do
  expect(subject).to receive(:bar).with("an argument I want")
  subject.foo
end
Wacko
источник
1
Извините, я не понимаю, как этот формат "to .. receive (: bar)" проверяет значение "called_bar" в этом примере. Вы можете мне это объяснить?
ecoding5,
2
@ ecoding5 нет. Это не так, и никто не должен проверять called_bar. Это был всего лишь флаг, гарантирующий, что метод был вызван, но expect(...).to receive(...)вы уже охватили это. Это более понятно и семантично
wacko
@wacko oooh, понял, спасибо, что прояснили это. С первого раза не поймал.
ecoding5,
37

Ниже должно работать

describe "#foo"
  it "should call 'bar' with appropriate arguments" do
     subject.stub(:bar)
     subject.foo
     expect(subject).to have_received(:bar).with("Invalid number of arguments")
  end
end

Документация: https://github.com/rspec/rspec-mocks#expecting-arguments

бджхаид
источник
Спасибо - я получал "NoMethodError" has_received? - думаю, это может быть связано с версиями rspec. Я нашел другое решение, которое сработало для меня (помеченное выше как правильное)
Майки Хогарт
@MikeyHogarth. Этот ответ предполагал have_received(постфактум «шпионский» подход), а не has_received, который не является частью какой-либо версии RSpec, которую я знаю.
Питер Альфвин
2

Чтобы полностью соответствовать синтаксису RSpec ~> 3.1 и rubocop-rspecопции по умолчанию для правила RSpec/MessageSpies, вот что вы можете сделать с spy:

Ожидания сообщения помещают ожидание примера в начало, прежде чем вы вызовете тестируемый код. Многие разработчики предпочитают использовать шаблон «упорядочить-действовать-утверждать» (или «задано-когда-то») для структурирования тестов. Шпионы - это альтернативный тип тестовых двойников, которые поддерживают этот шаблон, позволяя вам ожидать, что сообщение было получено постфактум, используя have_received.

# arrange.
invitation = spy('invitation')

# act.
invitation.deliver("foo@example.com")

# assert.
expect(invitation).to have_received(:deliver).with("foo@example.com")

Если вы не используете rubocop-rspec или используете нестандартную опцию. Конечно, вы можете использовать RSpec 3 по умолчанию с ожиданием.

dbl = double("Some Collaborator")
expect(dbl).to receive(:foo).with("foo@example.com")
И Цзэн
источник