Вопрос: Есть ли менее навязчивая альтернатива Rspec `should_receive`?


При написании тестов Rspec меня часто расстраивают should_receive, Я хотел бы знать, есть ли менее навязчивая альтернатива.

Например:

describe "making a cake" do
  it "should use some other methods" do
    @baker.should_receive(:make_batter)
    @baker.make_cake
  end
end

Призыв к should_receive это хорошее описание, но он нарушает мой код, потому что should_receive работает путем маскировки исходного метода и make_cake не может продолжаться, если make_batter фактически возвращает некоторое тесто. Поэтому я меняю это на это:

@baker.should_receive(:make_batter).and_return(@batter)

Это уродливо, потому что:

  • Это выглядит  как я тестирую это make_batter правильно возвращается @batter, но я на самом деле  заставляя поддельную версию make_batter чтобы вернуть это.
  • Это заставляет меня отдельно настраивать @batter
  • Если make_batter имеет какие-то важные побочные эффекты (что может быть запахом кода, я полагаю), я тоже должен это сделать.

я желаю это should_receive(:make_batter) будет проверять вызов метода и передать его оригинальному методу , Если бы я хотел заглушить его поведение для лучшего тестирования изоляции, я бы сделал это явно: @baker.stub(:make_batter).and_return(@batter),

Есть ли способ сделать что-то вроде should_receive без предупреждения вызова метода? Является ли моя проблема симптомом плохого дизайна?


42


источник


Ответы:


Похоже, что более удобный API для делегирования оригинальному методу, на который ссылался Майрон Марстон, фактически добавлен в rspec-mocks v2.12.0

Итак, теперь вы можете просто сделать это в любое время, когда вы «хотите установить выступление сообщения, не мешая тому, как объект отвечает на сообщение»:

@baker.should_receive(:make_batter).and_call_original

Спасибо, что добавил это, Мирон.


62



Вы можете иметь should_receive выполните оригинальный метод следующим образом:

@baker.should_receive(:make_batter, &@baker.method(:make_batter))

И то и другое should_receive а также stub поддержка передачи реализации блока (которая называется eval'd при вызове метода). &@baker.method(:make_batter) получает дескриптор исходного объекта метода и передает его как реализацию блока.

FWIW, мы хотели бы предоставить более удобный API для делегирования оригинальному методу (см. Эта проблема ), но сложно добавить эту функциональность, не нарушая совместимость.


11



У вас возникла проблема с should_receive потому что вы проверяете детали реализации make_cake метод. При написании тестов вы должны сосредоточиться только на поведении, а не на последовательности вызовов внутренних методов. В противном случае рефакторинг кода позже приведет к огромному рефакторингу всех ваших тестов.

Mocks and Stubs пригодится, когда вы хотите изолировать свои классы. При написании модульных тестов ваш испытуемый объект должен быть изолирован от любого другого объекта. Оба выступают в качестве stand-in, когда вы работаете с несколькими объектами одновременно. В этом случае вы можете использовать should_receive чтобы ваш испытуемый правильно передал какую-либо задачу другому объекту, вызвав метод.


6