Rails 3: Наблюдатель ActiveRecord: обратный вызов after_commit не срабатывает во время тестов, но after_save срабатывает

У меня есть приложение Rails 3. Я использую обратные вызовы after_save для некоторых моделей и обратные вызовы after_commit для одной из моделей. Весь код работает нормально, но во время тестов RSpec обратный вызов after_commit не вызывается, когда я сохраняю модель Thing.

e.g.

class ThingObserver  <  ActiveRecord:Observer
  observe Thing
  def after_commit(thing)
    puts thing.inspect
  end
end

Если я изменю имя метода на after_save, он будет нормально вызываться во время тестов. Мне нужно иметь возможность использовать after_commit для этой конкретной модели, потому что в некоторых ситуациях изменение «вещи» происходит на веб-сервере, но эффект наблюдателя происходит в работнике Sidekiq, и after_save не гарантирует, что данные было совершено и доступно, когда рабочий готов к этому.

Конфигурация для RSpec выглядит так в spec / spec_helper.rb

Rspec.configure do |config|
  #yada yada
  config.use_transactional_fixtures = true
  #yada yada
end

Я также скорректировал rake db:create так, чтобы он извлекался из файла structure.sql. В lib / tasks / db.rb

task setup: [ 'test:ensure_environment_is_test', 'db:create', 'db:structure:load', 'db:migrate', 'db:seed' ]

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

Есть ли способ запустить как обратные вызовы after_save, так и after_commit, не делая Rspec use_transactional_fixtures == false?

Или есть способ установить config.use_transactional_fixtures в 'false' только для этого теста или для этого тестового файла?


person Jay Godse    schedule 25.11.2014    source источник


Ответы (1)


Чтобы правильно выполнять коммиты базы данных, вам необходимо включить config.use_transactional_fixtures, но я бы рекомендовал вам рассмотреть разные стратегии, поскольку этот параметр по умолчанию отключен для хорошего дизайна тестирования, чтобы ваши тесты были как можно более унитарными и изолированными.

Во-первых, вы можете запускать обратные вызовы ActiveRecord с #run_callbacks(type), в вашем случае model.run_callbacks(:commit)

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

class Person
  after_commit :register_birth

  def register_birth
    # your code
  end
end

describe Person do
  describe "registering birth" do
    it "registers ..." do
    end

    it "runs after database insertion" do
      expect(model).to receive(:register_birth)
      model.run_callbacks(:commit)
    end
  end
end

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

Но, в конце концов, вы знаете свой домен и требования к дизайну лучше, чем незнакомец, поэтому, если вам действительно нужен after_commit для запуска, вы можете принудительно запустить его с помощью model.run_callbacks(:commit). Просто инкапсулируйте это на своем заводе / приспособлении, и вам не нужно помнить об этом каждый раз.

person rafb3    schedule 26.11.2014
comment
не уверен, увидит ли кто-нибудь это, но мне было интересно, знает ли кто-нибудь, что это значит, если run_callbacks (: commit) ничего не делает, например. журнал test.log пуст для вызова этого метода? - person Ben; 28.12.2014
comment
Что ты имеешь в виду? Вы регистрируете что-нибудь внутри метода обратного вызова? Также это on: :update? @Бен - person rafb3; 30.12.2014
comment
У меня была проблема, когда моя модель не фиксировалась в базе данных (оказывается, это ограничивал очиститель базы данных). поэтому я попытался выполнить run_Callback (: commit), чтобы убедиться, что модель фиксируется. но ничего не произошло? - person Ben; 05.01.2015
comment
Обратные вызовы не фиксируются, просто запустите хуки, которые настроены с after/before_commit - person rafb3; 05.01.2015