Rails 3:ActiveRecord观察者:after_commit回调在测试期间没有触发,但是after_save确实触发了

时间:2014-11-25 23:56:16

标签: ruby-on-rails ruby-on-rails-3 rspec observers

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

e.g。

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

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

RSpec的配置在spec / spec_helper.rb

中如下所示
after_save

我还调整了 Rspec.configure do |config| #yada yada config.use_transactional_fixtures = true #yada yada end ,以便从structure.sql文件中提取。在lib / tasks / db.rb

rake db:create

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

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

或者,有没有办法将config.use_transactional_fixtures设置为'false'仅用于该测试或该测试文件?

1 个答案:

答案 0 :(得分:4)

要正确执行数据库提交,您需要启用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)强制它。只需将其封装在您的工厂/夹具上,您就不必每次都记住它。