Пропустить обратные вызовы на Factory Girl и Rspec - PullRequest
94 голосов
/ 06 января 2012

Я тестирую модель с обратным вызовом после создания, который я хотел бы запускать только в некоторых случаях во время тестирования.Как я могу пропустить / запустить обратные вызовы с завода?

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

Ответы [ 15 ]

2 голосов
/ 04 апреля 2019

Rails 5 - skip_callback повышение аргумента ошибки при пропуске с фабрики FactoryBot.

ArgumentError: After commit callback :whatever_callback has not been defined

Произошло изменение в Rails 5 с тем, как skip_callback обрабатывает нераспознанные обратные вызовы:

ActiveSupport :: Callbacks # skip_callback теперь вызывает ArgumentError, если неопознанный обратный вызов удаляется

Когда с фабрики вызывается skip_callback, реальный обратный вызов в модели AR еще не определен.

Если вы попробовали все и вытащили свои волосы, как я, вот ваше решение (получено в результате поиска проблем FactoryBot) ( ПРИМЕЧАНИЕ raise: false part ):

after(:build) { YourSweetModel.skip_callback(:commit, :after, :whatever_callback, raise: false) }

Не стесняйтесь использовать его с любыми другими стратегиями, которые вы предпочитаете.

2 голосов
/ 31 августа 2018

Что касается ответа, опубликованного выше, https://stackoverflow.com/a/35562805/2001785, вам не нужно добавлять код на завод.Мне было легче перегружать методы в самих спецификациях.Например, вместо (в сочетании с заводским кодом в цитируемом сообщении)

let(:user) { FactoryGirl.create(:user) }

мне нравится использовать (без цитированного заводского кода)

let(:user) do
  FactoryGirl.build(:user).tap do |u|
      u.define_singleton_method(:send_welcome_email){}
      u.save!
    end
  end
end

Таким образом, вы не делаетенужно посмотреть как заводские, так и тестовые файлы, чтобы понять поведение теста.

2 голосов
/ 06 января 2012

В моем случае у меня есть функция обратного вызова, загружающая что-то в мой Redis-кеш. Но тогда у меня не было / не нужно запускать экземпляр redis для моей тестовой среды.

after_create :load_to_cache

def load_to_cache
  Redis.load_to_cache
end

Для моей ситуации, аналогичной описанной выше, я просто вставил свой метод load_to_cache в мой spec_helper, с:

Redis.stub(:load_to_cache)

Кроме того, в определенной ситуации, когда я хочу проверить это, мне просто нужно отменить их вставку в блоке before соответствующих тестовых примеров Rspec.

Я знаю, что у вас может быть что-то более сложное, что происходит в вашем after_create, или вы не можете найти это очень элегантным. Вы можете попытаться отменить обратный вызов, определенный в вашей модели, определив хук after_create в вашей фабрике (см. Документацию factory_girl), где вы, вероятно, можете определить тот же обратный вызов и вернуть false в соответствии с 'Отмена обратных вызовов 'раздел этой статьи . (Я не уверен в порядке, в котором выполняется обратный вызов, поэтому я не выбрал этот вариант).

Наконец, (извините, я не могу найти статью) Ruby позволяет вам использовать грязное метапрограммирование, чтобы отцепить ловушку обратного вызова (вам придется ее сбросить). Я думаю, это будет наименее предпочтительный вариант.

Ну, есть еще одна вещь, не совсем решение, но посмотрим, сможете ли вы обойтись без Factory.build в ваших спецификациях, вместо того, чтобы фактически создавать объект. (Было бы проще всего, если вы можете).

0 голосов
/ 02 мая 2018

Я нашел следующее решение более чистым, поскольку обратный вызов запускается / устанавливается на уровне класса.

# create(:user) - will skip the callback.
# create(:user, skip_create_callback: false) - will set the callback
FactoryBot.define do
  factory :user do
    first_name "Luiz"
    last_name "Branco"

    transient do
      skip_create_callback true
    end

    after(:build) do |user, evaluator|
      if evaluator.skip_create_callback
        user.class.skip_callback(:create, :after, :run_something)
      else
        user.class.set_callback(:create, :after, :run_something)
      end
    end
  end
end
0 голосов
/ 27 июня 2016
FactoryGirl.define do
 factory :user do
   first_name "Luiz"
   last_name "Branco"
   #...

after(:build) { |user| user.class.skip_callback(:create, :after, :run_something) }

trait :user_with_run_something do
  after(:create) { |user| user.class.set_callback(:create, :after, :run_something) }
  end
 end
end

Вы можете просто установить обратный вызов с чертой для тех случаев, когда вы хотите запустить его.

...