#ruby #rspec
#ruby #rspec
Вопрос:
Я тестирую класс, который использует клиент, который выполняет внешние запросы, и я хотел бы издеваться над этим клиентом, но убедитесь, что он вызывается, однако я получаю двойную ошибку.
Мой тест выглядит примерно так:
describe '#execute' do
let(:attributes) { {foo: 'bar'} }
let(:client_double) { double('client', create: nil) }
let(:use_case) { described.class.new }
before do
allow(Client::Base).to receive(:new).and_return(client_double)
use_case.execute(attributes)
end
it 'creates something' do
expect(Something.find_by(foo: 'bar')).not_to be_nil
end
it 'calls client' do
expect(client).to have_received(:create).with('bar')
end
end
и первый пример проходит, как и ожидалось, однако rspec продолжает ломаться во втором примере, выдавая мне эту ошибку:
#<Double "foo"> was originally created in one example but has leaked into another example and can no longer be used. rspec-mocks' doubles are designed to only last for one example, and you need to create a new one in each example you wish to use it for.
кто-нибудь знает, что я могу сделать, чтобы это исправить?
Ответ №1:
Повторное использование приспособлений с помощью методов Let
В данном случае, before
на самом деле before(:each)
, это повторное использование client_double и атрибутов, которые вы определили с помощью вспомогательного метода #let . let
Команды делают эти переменные функционально эквивалентными переменным экземпляра в области описываемого объекта, поэтому вы на самом деле не тестируете свежесозданные объекты в каждом примере.
Некоторые альтернативы включают:
- Проведите рефакторинг, чтобы поместить все ваши настройки в
before(:each)
без операторов let. - Сделайте ваши тесты ВЛАЖНЫМИ, выполнив дополнительные настройки в каждом примере.
- Настройте новую область для нового #describe , чтобы ваши тестовые удвоения / значения не использовались повторно.
- При необходимости используйте ваши блоки :before , :after или :around для сброса состояния между тестами.
Поскольку вы не показываете фактический класс или реальный тестируемый код, трудно предложить конкретное представление о правильном способе тестирования объекта, который вы пытаетесь протестировать. Даже не ясно, почему вы считаете, что вам нужно протестировать объект collaborator в модульном тестировании, поэтому вы можете подумать и об этом.
Комментарии:
1. Спасибо, Тодд, за действительно хороший ответ, но оказывается, это потому, что клиент был одноэлементным, а я еще не заметил.. поэтому вместо того, чтобы издеваться над методом :new, я издевался над методом:instance, и все заработало.
Ответ №2:
Оказывается, я использовал синглтон в качестве клиента и раньше не осознавал, так что это было действительно кэширование класса с помощью примеров. Чтобы исправить это, все, что я сделал, это издевался над instantiate
методом вместо new
метода, и все сработало.
Итак, в конце концов это сработало:
allow(Client::Base).to receive(:instantiate).and_return(client_double)