跳过Factory Girl和Rspec的callback
我正在使用after createcallback来testing一个模型,我只想在testing的时候在某些场合运行。 我怎样才能跳过/运行工厂callback?
class User < ActiveRecord::Base after_create :run_something ... end
厂:
FactoryGirl.define do factory :user do first_name "Luiz" last_name "Branco" ... # skip callback factory :with_run_something do # run callback end end
我不确定这是否是最好的解决scheme,但是我已经成功地实现了这一点:
FactoryGirl.define do factory :user do first_name "Luiz" last_name "Branco" #... after(:build) { |user| user.class.skip_callback(:create, :after, :run_something) } factory :user_with_run_something do after(:create) { |user| user.send(:run_something) } end end end
无callback运行:
FactoryGirl.create(:user)
以callback运行:
FactoryGirl.create(:user_with_run_something)
当你不想运行callback时,请执行以下操作:
User.skip_callback(:create, :after, :run_something) Factory.create(:user)
请注意,skip_callback在运行后会在其他规范中持续存在,因此请考虑以下内容:
before do User.skip_callback(:create, :after, :run_something) end after do User.set_callback(:create, :after, :run_something) end
我想对@luizbranco的回答作出改进,以便在创build其他用户时使after_savecallback更加可重用。
FactoryGirl.define do factory :user do first_name "Luiz" last_name "Branco" #... after(:build) { |user| user.class.skip_callback(:create, :after, :run_something1, :run_something2) } trait :with_after_save_callback do after(:build) { |user| user.class.set_callback(:create, :after, :run_something1, :run_something2) } end end end
无after_savecallback运行:
FactoryGirl.create(:user)
使用after_savecallback运行:
FactoryGirl.create(:user, :with_after_save_callback)
在我的testing中,我更喜欢在没有默认callback的情况下创build用户,因为所使用的方法会运行额外的东西,我通常不需要在我的testing示例中。
———- UPDATE ————我停止使用skip_callback,因为在testing套件中有一些不一致的问题。
替代解决scheme1(使用存根和未读):
after(:build) { |user| user.class.any_instance.stub(:run_something1) user.class.any_instance.stub(:run_something2) } trait :with_after_save_callback do after(:build) { |user| user.class.any_instance.unstub(:run_something1) user.class.any_instance.unstub(:run_something2) } end
替代解决scheme2(我的首选方法):
after(:build) { |user| class << user def run_something1; true; end def run_something2; true; end end } trait :with_after_save_callback do after(:build) { |user| class << user def run_something1; super; end def run_something2; super; end end } end
这些解决scheme都不是很好。 他们通过删除应该从实例中删除的function,而不是从类中删除的function来污损class级。
factory :user do before(:create){|user| user.define_singleton_method(:send_welcome_email){}}
而不是抑制callback,我压制callback的function。 在某种程度上,我更喜欢这种方法,因为它更加明确。
从我的工厂调用skip_callbackcertificate对我来说是有问题的。
在我的情况下,我有一个文件类与一些s3相关的callback在创build之前和之后,我只想运行时testing完整的堆栈是必要的。 否则,我想跳过这些S3callback。
当我在我的工厂尝试skip_callbacks时,即使我直接创build了一个文档对象,也没有使用工厂,它仍然保留了callback跳过。 所以相反,我使用摩卡桩在后build立呼叫,一切都工作完美:
factory :document do upload_file_name "file.txt" upload_content_type "text/plain" upload_file_size 1.kilobyte after(:build) do |document| document.stubs(:name_of_before_create_method).returns(true) document.stubs(:name_of_after_create_method).returns(true) end end
这将使用目前的rspec语法(截至这篇文章),是更清洁:
before do User.any_instance.stub :run_something end
一个简单的存根在Rspec 3中最适合我
allow(User).to receive_messages(:run_something => nil)
这个解决scheme适用于我,而且您不必为Factory定义添加额外的块:
user = FactoryGirl.build(:user) user.send(:create_without_callbacks) # Skip callback user = FactoryGirl.create(:user) # Execute callbacks
在我的情况下,我有callback加载到我的rediscaching。 但是,我没有/想要为我的testing环境运行redis实例。
after_create :load_to_cache def load_to_cache Redis.load_to_cache end
对于我的情况,类似于上面,我只是在我的spec_helper中存储了我的load_to_cache
方法:
Redis.stub(:load_to_cache)
此外,在某些情况下,我想要testing这个,我只需要在相应的Rspectesting用例的前面块中取消它们。
我知道你可能有更复杂的事情发生在你的after_create
或者可能不会觉得这非常优雅。 您可以尝试取消您的模型中定义的callback,方法是在Factory中定义一个after_create
挂钩(请参阅after_create
文档),您可以根据此处的“取消callback”部分定义相同的callback并返回false
文章 。 (我不确定执行callback的顺序,这就是为什么我没有去这个选项)。
最后,(对不起,我找不到这篇文章)Ruby允许你使用一些脏的元编程来解除一个callback钩子(你将不得不重置它)。 我想这将是最不推荐的select。
那么还有一件事情,并不是一个真正的解决scheme,但是看看你能否在规范中脱离Factory.build,而不是实际创build对象。 (如果可以的话,会是最简单的)。
James Chevalier关于如何跳过before_validationcallback的答案并没有帮助我,所以如果你像我一样徘徊在这里工作的解决scheme:
在模型中:
before_validation :run_something, on: :create
在工厂:
after(:build) { |obj| obj.class.skip_callback(:validation, :before, :run_something) }
FactoryGirl.define do factory :order, class: Spree::Order do trait :without_callbacks do after(:build) do |order| order.class.skip_callback :save, :before, :update_status! end after(:create) do |order| order.class.set_callback :save, :before, :update_status! end end end end
重要提示你应该指定他们两个。 如果只使用之前和运行多个规格,它会尝试多次禁用callback。 第一次会成功,但第二次,callback不会被定义。 所以会出错
FactoryGirl.define do factory :user do first_name "Luiz" last_name "Branco" #... after(:build) { |user| user.class.skip_callback(:create, :after, :run_something) } trait :user_with_run_something do after(:create) { |user| user.class.set_callback(:create, :after, :run_something) } end end end
当你想运行这些实例时,你可以设置callback的特性。