Не используйте subject для испытаний
Бывает, вижу в проектах именованные сабжекты в качестве хелперов для испытаний:
subject(:process) { described_class.new.process }
before do
allow(EventProducer).to receive(:emit).and_return(true)
end
context "when run twice" do
it "emits only one event" do
process
process
expect(EventProducer).to have_received(:emit).once
end
end
Это плохой, ложноположительный тест. Что бы мы не написали внутри process, EventProducer.emit всегда будет вызываться точно один раз.
Проблема в хелпере subject. Как и let, он кеширующий, мемоизирующий. После первого вызова он запоминает результат блока и в последующие вызовы сразу возвращает результат, не тригеря код в блоке.
Это легко проверить мини-тестом:
class EventProducer
def self.emit
end
end
describe "subject(:process)" do
subject(:process) { EventProducer.emit("foo") }
before { allow(EventProducer).to receive(:emit) }
context "when run twice" do
it "emits both events" do
process
process
expect(EventProducer).to have_received(:emit).twice
end
end
end
Результат:
Failures:
1) subject(:process) when run twice emits both events
Failure/Error: expect(EventProducer).to have_received(:emit).twice
(EventProducer (class)).emit(*(any args))
expected: 2 times with any arguments
received: 1 time with any arguments
# ./spec.rb:17:in `block (3 levels) in <top (required)>'
Пожалуйста, не используйте именованные сабжекты для испытаний. Лучше сделайте старый добрый метод:
def process
described_class.new.process
end
P. S. Ещё больше постов о программировании, тестах и культуре разработки у меня в Телеграме.