Подтвердить что ты не робот

Существует ли менее интрузивная альтернатива 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, не предотвращая вызов исходного метода? Является ли моя проблема симптомом плохого дизайна?

4b9b3361

Ответ 1

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

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

@baker.should_receive(:make_batter).and_call_original

Спасибо за добавление этого, Myron.

Ответ 2

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

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

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

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

Ответ 3

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

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