Я тестирую модель с обратным вызовом после создания, которую я хотел бы запускать только в некоторых случаях во время тестирования. Как я могу пропустить / запустить обратные вызовы с завода?
class User < ActiveRecord::Base
after_create :run_something
...
end
Завод:
FactoryGirl.define do
factory :user do
first_name "Luiz"
last_name "Branco"
...
# skip callback
factory :with_run_something do
# run callback
end
end
ruby-on-rails
rspec
factory-bot
Луисбранко
источник
источник
:on => :create
валидацию, используйтеafter(:build) { |user| user.class.skip_callback(:validate, :create, :after, :run_something) }
Class.skip_callback
вызов будет постоянным в других тестах, поэтому, если другие ваши тесты ожидают, что обратный вызов произойдет, они потерпят неудачу, если вы попытаетесь инвертировать логику пропуска обратного вызова.after(:build)
блоке. Это позволяет заводским настройкам по умолчанию запускать обратный вызов и не требует сброса обратного вызова после каждого использования.Если вы не хотите запускать обратный вызов, сделайте следующее:
Имейте в виду, что skip_callback будет постоянным в других спецификациях после его запуска, поэтому рассмотрите следующее:
источник
Ни одно из этих решений не является хорошим. Они искажают класс, удаляя функциональные возможности, которые должны быть удалены из экземпляра, а не из класса.
Вместо того, чтобы подавлять обратный вызов, я подавляю функциональность обратного вызова. В некотором смысле мне больше нравится этот подход, потому что он более ясен.
источник
around_*
(напримерuser.define_singleton_method(:around_callback_method){|&b| b.call }
).Я хотел бы улучшить ответ @luizbranco, чтобы сделать обратный вызов after_save более повторно используемым при создании других пользователей.
Запуск без обратного вызова after_save:
Запуск с обратным вызовом after_save:
В моем тесте я предпочитаю создавать пользователей без обратного вызова по умолчанию, потому что используемые методы запускают дополнительные вещи, которые я обычно не хочу в моих тестовых примерах.
---------- ОБНОВЛЕНИЕ ------------ Я перестал использовать skip_callback, потому что в наборе тестов были проблемы с несогласованностью.
Альтернативное решение 1 (использование заглушки и заглушки):
Альтернативное решение 2 (мой предпочтительный подход):
источник
Rails 5 -
skip_callback
возникает ошибка аргумента при переходе с фабрики FactoryBot.В Rails 5 произошло изменение в том, как skip_callback обрабатывает нераспознанные обратные вызовы:
Когда
skip_callback
вызывается с завода, реальный обратный вызов в модели AR еще не определен.Если вы все перепробовали и вытащили волосы, как я, вот ваше решение (взято из поиска проблем FactoryBot) ( ЗАПОМНИТЕ эту
raise: false
часть ):Не стесняйтесь использовать его с любыми другими стратегиями, которые вам нравятся.
источник
Это решение работает для меня, и вам не нужно добавлять дополнительный блок в определение Factory:
источник
Важное примечание: вы должны указать их оба. Если использовать только ранее и запустить несколько спецификаций, он попытается отключить обратный вызов несколько раз. В первый раз это будет успешно, но во второй обратный вызов больше не будет определяться. Так что это ошибка
источник
В Rspec 3 мне больше всего подошла простая заглушка
источник
User
;:run_something
не является методом класса.Вызов skip_callback с моего завода оказался для меня проблематичным.
В моем случае у меня есть класс документа с некоторыми обратными вызовами, связанными с s3, до и после создания, которые я хочу запускать только тогда, когда необходимо тестирование полного стека. В противном случае я хочу пропустить эти обратные вызовы s3.
Когда я попробовал skip_callbacks на своей фабрике, он сохранил этот пропуск обратного вызова, даже когда я создал объект документа напрямую, без использования фабрики. Поэтому вместо этого я использовал заглушки мокко в вызове после сборки, и все работает отлично:
источник
before_validation
крючком (пытается сделатьskip_callback
с любым из FactoryGirl - хbefore
илиafter
вариантовbuild
иcreate
не работают)Это будет работать с текущим синтаксисом rspec (начиная с этого сообщения) и намного чище:
источник
Ответ Джеймса Шевалье о том, как пропустить обратный вызов before_validation, мне не помог, поэтому, если вы отказываетесь от того же, что и я, вот рабочее решение:
в модели:
на заводе:
источник
Model.skip_callback(...)
В моем случае у меня есть обратный вызов, загружающий что-то в мой кеш Redis. Но тогда у меня не было / не нужно было запускать экземпляр Redis для моей тестовой среды.
Для моей ситуации, аналогичной описанной выше, я просто вставил свой
load_to_cache
метод в свой spec_helper с помощью:Кроме того, в определенной ситуации, когда я хочу проверить это, мне просто нужно отключить их в блоке before соответствующих тестовых случаев Rspec.
Я знаю, что у вас может происходить что-то более сложное,
after_create
или это может показаться не очень элегантным. Вы можете попытаться отменить обратный вызов, определенный в вашей модели, определивafter_create
ловушку в своем Factory (см. Документацию factory_girl), где вы, вероятно, можете определить тот же обратный вызов и возвратfalse
, согласно разделу «Отмена обратных вызовов» этой статьи . (Я не уверен в том, в каком порядке выполняются обратные вызовы, поэтому я не выбрал этот вариант).Наконец, (извините, я не могу найти статью) Ruby позволяет вам использовать грязное мета-программирование, чтобы отцепить перехватчик обратного вызова (вам придется его сбросить). Думаю, это наименее предпочтительный вариант.
Что ж, есть еще одна вещь, на самом деле не решение, но посмотрите, сможете ли вы уйти с Factory.build в своих спецификациях вместо фактического создания объекта. (Было бы проще, если бы можно).
источник
Что касается ответа, опубликованного выше, https://stackoverflow.com/a/35562805/2001785 , вам не нужно добавлять код на заводе. Мне было проще перегрузить методы в самих спецификациях. Например, вместо (в сочетании с заводским кодом в цитируемом посте)
Мне нравится использовать (без указанного заводского кода)
Таким образом, вам не нужно смотреть как на фабрику, так и на тестовые файлы, чтобы понять поведение теста.
источник
Я нашел следующее решение более чистым, поскольку обратный вызов запускается / устанавливается на уровне класса.
источник
Вот фрагмент, который я создал, чтобы справиться с этим общим способом.
Он пропустит все настроенные обратные вызовы, включая обратные вызовы, связанные с рельсами, например
before_save_collection_association
, но он не пропустит некоторые, необходимые для нормальной работы ActiveRecord, например автоматически сгенерированныеautosave_associated_records_for_
обратные вызовы.тогда позже:
Излишне говорить, что YMMV, так что посмотрите в журналах тестов, что вы действительно пропускаете. Возможно, у вас есть драгоценный камень, добавляющий обратный вызов, который вам действительно нужен, и он заставит ваши тесты с треском провалиться, или из вашей жирной модели из 100 обратных вызовов вам просто нужна пара для конкретного теста. В таких случаях попробуйте переходный
:force_callbacks
БОНУС
Иногда вам нужно также пропустить проверки (все, чтобы сделать тесты быстрее), а затем попробуйте:
источник
Вы можете просто установить обратный вызов с трейтом для тех экземпляров, когда захотите его запустить.
источник