Rails 3:ActiveRecord观察者:after_commit回调在测试期间不会触发,但是after_save会触发

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

例如

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

如果我将方法名称更改为after_save ,则在测试期间将其调用为fine。 我需要能够为此特定模型使用after_commit ,因为在某些情况下,对“事物”的更改发生在Web服务器中,但观察者的效果发生在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’仅用于该测试或该测试文件?

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

首先,您可以使用#run_callbacks(type)运行ActiveRecord回调,在您的案例中是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)强制它。 只需将其封装在您的工厂/夹具上,您就不必每次都记住它。