Rails 3:ActiveRecord 观察者:after_commit 回调在测试期间不会触发,但 after_save 会触发 [英] Rails 3: ActiveRecord observer: after_commit callback doesn't fire during tests, but after_save does fire

查看:39
本文介绍了Rails 3:ActiveRecord 观察者:after_commit 回调在测试期间不会触发,但 after_save 会触发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 Rails 3 应用程序.我对某些模型使用 after_save 回调,对其中一个模型使用 after_commit 回调.所有代码都可以正常工作,但在 RSpec 测试期间,当我保存 Thing 模型时,不会调用 after_commit 回调.

I have a Rails 3 application. I use after_save callbacks for some models and after_commit callbacks for one of the models. All of the code the code works fine, but during RSpec tests, the after_commit callback doesn't get called when I save a Thing model.

例如

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

如果我将方法名称更改为 after_save,它会在测试期间被很好地调用.我需要能够将 after_commit 用于此特定模型,因为在某些情况下,事物"的更改发生在 Web 服务器中,但观察者的效果发生在 Sidekiq 工作器中,并且 after_save 不保证数据在工作人员准备好时已提交并可用.

If I change the method name to after_save, it gets called fine during tests. I need to be able to use after_commit for this particular model because in some situations the change to a "thing" happens in a web server, but the effect of the observer happens in a Sidekiq worker, and after_save doesn't guarantee that the data was committed and available when the worker is ready for it.

RSpec 的配置在 spec/spec_helper.rb 中如下所示

The config for RSpec looks like this in 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 中

I have also adjusted rake db:create so that it draws from a structure.sql file. In lib/tasks/db.rb

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

我这样做是为了我可以运行测试以确保数据库强制执行外键约束.

I did this so that I could run tests to ensure that the database was enforcing foreign key constraints.

有没有办法在不使 Rspec use_transactional_fixtures == false 的情况下同时运行 after_save 和 after_commit 回调?

Is there a way to run both the after_save and after_commit callbacks without making the Rspec use_transactional_fixtures == false?

或者,有没有办法仅针对该测试或该测试文件将 config.use_transactional_fixtures 设置为false"?

Or, is there a way to set config.use_transactional_fixtures to 'false' just for that test, or for that test file?

推荐答案

要正确执行数据库提交,您需要启用 config.use_transactional_fixtures,但我建议您考虑不同的策略,因为该选项是为了良好的测试设计,默认情况下禁用,以强制您的测试尽可能统一和隔离.

To properly execute database commits you need to enable config.use_transactional_fixtures, but I'd recommend you consider different strategies as that option is disabled by default for the sake of good test design, to enforce your tests to be as unitary and isolated as possible.

首先,您可以使用 #run_callbacks(type) 运行 ActiveRecord 回调,在您的情况下为 model.run_callbacks(:commit)

First, you can run ActiveRecord callbacks with #run_callbacks(type), in your case model.run_callbacks(:commit)

我的首选策略是有一个方法和你想要运行的逻辑,然后使用方法名声明钩子,然后通过直接调用它来测试方法行为,并测试在运行钩子时调用该方法.

My preferred strategy is to have a method with the logic you want to run, then declare the hook using the method name, then test the method behavior by calling it directly and test that the method is called when the hook is run.

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

这假设您在回调中拥有的任何逻辑对于模型状态都不是必需的,即不会将其更改为您需要立即使用的东西,并且与它交互的任何其他模型都对其无动于衷.因此不需要在测试上下文中运行.这是一个强大的设计原则,从长远来看,通过要求一些与您正在测试的单元无关的属性被设置为仅在您不关心的回调上使用,从而防止回调失控并为测试生成依赖项大约在那一刻.

That assumes that whatever logic you have on the callback is not essential for the model state, i.e. doesn't change it to something you need to consume right away, and that any other model that interacts with it is indifferent to it. And as such isn't required to run in a test context. That is a powerful design principle that in the long term prevents callbacks to get out of control and generate dependencies for tests by demanding some property unrelated to the unit you are testing to be setup only to be consumed on a callback that you don't care about at that moment.

但是,最终您比陌生人更了解您的领域和设计要求,因此,如果您确实需要 after_commit 运行,您可以使用 model.run_callbacks(:提交).只需将其封装在您的工厂/夹具上,您就不必每次都记住它.

But, in the end you know your domain and design requirements better than a stranger so, if you really need the after_commit to run, you can force it with model.run_callbacks(:commit). Just encapsulate that on your factory/fixture and you don't have to remember it every time.

这篇关于Rails 3:ActiveRecord 观察者:after_commit 回调在测试期间不会触发,但 after_save 会触发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆