Ruby-on-rails 跳过 Factory Girl 和 Rspec 的回调
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8751175/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Skip callbacks on Factory Girl and Rspec
提问by luizbranco
I'm testing a model with an after create callback that I'd like to run only on some occasions while testing. How can I skip/run callbacks from a factory?
我正在测试一个带有创建后回调的模型,我只想在测试时在某些情况下运行该回调。如何跳过/运行工厂的回调?
class User < ActiveRecord::Base
after_create :run_something
...
end
Factory:
工厂:
FactoryGirl.define do
factory :user do
first_name "Luiz"
last_name "Branco"
...
# skip callback
factory :with_run_something do
# run callback
end
end
回答by luizbranco
I'm not sure if it is the best solution, but I have successfully achieved this using:
我不确定这是否是最好的解决方案,但我已经成功地实现了这一点:
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
Running without callback:
无回调运行:
FactoryGirl.create(:user)
Running with callback:
运行回调:
FactoryGirl.create(:user_with_run_something)
回答by Minimul
When you don't want to run a callback do the following:
当您不想运行回调时,请执行以下操作:
User.skip_callback(:create, :after, :run_something)
Factory.create(:user)
Be aware that skip_callback will be persistant across other specs after it is run therefore consider something like the following:
请注意,skip_callback 在运行后将在其他规范中保持不变,因此请考虑以下内容:
before do
User.skip_callback(:create, :after, :run_something)
end
after do
User.set_callback(:create, :after, :run_something)
end
回答by B Seven
None of these solutions are good. They deface the class by removing functionality that should be removed from the instance, not from the class.
这些解决方案都不好。他们通过删除应该从实例中删除的功能来破坏类,而不是从类中删除。
factory :user do
before(:create){|user| user.define_singleton_method(:send_welcome_email){}}
Instead of suppressing the callback, I am suppressing the functionality of the callback. In a way, I like this approach better because it is more explicit.
我不是抑制回调,而是抑制回调的功能。在某种程度上,我更喜欢这种方法,因为它更明确。
回答by konyak
I'd like to make an improvement to @luizbranco 's answer to make after_save callback more reusable when creating other users.
我想对 @luizbranco 的答案进行改进,使 after_save 回调在创建其他用户时更易于重用。
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
Running without after_save callback:
在没有 after_save 回调的情况下运行:
FactoryGirl.create(:user)
Running with after_save callback:
使用 after_save 回调运行:
FactoryGirl.create(:user, :with_after_save_callback)
In my test, I prefer to create users without the callback by default because the methods used run extra stuff I don't normally want in my test examples.
在我的测试中,我更喜欢在默认情况下创建没有回调的用户,因为所使用的方法会运行我在测试示例中通常不想要的额外内容。
----------UPDATE------------ I stopped using skip_callback because there were some inconsistency issues in the test suite.
----------更新------------ 我停止使用skip_callback,因为测试套件中存在一些不一致的问题。
Alternative Solution 1 (use of stub and unstub):
替代解决方案 1(使用 stub 和 unstub):
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
Alternative Solution 2 (my preferred approach):
替代解决方案 2(我的首选方法):
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
回答by RudyOnRails
Rails 5 - skip_callbackraising Argument error when skipping from a FactoryBot factory.
Rails 5 -skip_callback从 FactoryBot 工厂跳过时引发参数错误。
ArgumentError: After commit callback :whatever_callback has not been defined
There was a change in Rails 5with how skip_callback handles unrecognized callbacks:
Rails 5 中的skip_callback 如何处理无法识别的回调发生了变化:
ActiveSupport::Callbacks#skip_callback now raises an ArgumentError if an unrecognized callback is remove
如果删除无法识别的回调,ActiveSupport::Callbacks#skip_callback 现在会引发 ArgumentError
When skip_callbackis called from the factory, the real callback in the AR model is not yet defined.
当skip_callback从工厂调用时,AR 模型中真正的回调尚未定义。
If you've tried everything and pulled your hair out like me, here is your solution (got it from searching FactoryBot issues)(NOTE the raise: falsepart):
如果您已经尝试了所有方法并像我一样将头发拔掉,那么这是您的解决方案(通过搜索 FactoryBot 问题获得)(注意raise: false部分):
after(:build) { YourSweetModel.skip_callback(:commit, :after, :whatever_callback, raise: false) }
Feel free to use it with whatever other strategies you prefer.
随意将它与您喜欢的任何其他策略一起使用。
回答by auralbee
This solution works for me and you don′t have to add an additional block to your Factory definition:
这个解决方案对我有用,你不必在你的工厂定义中添加额外的块:
user = FactoryGirl.build(:user)
user.send(:create_without_callbacks) # Skip callback
user = FactoryGirl.create(:user) # Execute callbacks
回答by AndreiMotinga
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
Important noteyou should specify both of them. If only use before and run multiple specs, it'll try to disable callback multiple times. It'll succeed the first time, but on the second, callback isn't going to be defined anymore. So it'll error out
重要说明您应该同时指定它们。如果只使用 before 并运行多个规范,它会尝试多次禁用回调。第一次会成功,但第二次,回调将不再被定义。所以它会出错
回答by samg
A simple stub worked best for me in Rspec 3
一个简单的存根在 Rspec 3 中最适合我
allow(User).to receive_messages(:run_something => nil)
回答by uberllama
Calling skip_callback from my factory proved problematic for me.
从我的工厂调用 skip_callback 对我来说是有问题的。
In my case, I have a document class with some s3-related callbacks in before and after create that I only want to run when testing the full stack is necessary. Otherwise, I want to skip those s3 callbacks.
就我而言,我有一个文档类,在创建之前和之后有一些与 s3 相关的回调,我只想在需要测试完整堆栈时运行。否则,我想跳过那些 s3 回调。
When I tried skip_callbacks in my factory, it persisted that callback skip even when I created a document object directly, without using a factory. So instead, I used mocha stubs in the after build call and everything is working perfectly:
当我在我的工厂中尝试 skip_callbacks 时,即使我直接创建了一个文档对象,它也坚持了回调跳过,而不使用工厂。因此,我在 after build 调用中使用了 mocha 存根,并且一切正常:
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
回答by Zyren
This will work with current rspec syntax (as of this post) and is much cleaner:
这将适用于当前的 rspec 语法(截至本文)并且更清晰:
before do
User.any_instance.stub :run_something
end

