我有一个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'仅用于该测试或该测试文件?
答案 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)
强制它。只需将其封装在您的工厂/夹具上,您就不必每次都记住它。