Ruby-on-rails 如何独立测试模型的回调方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16677718/
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
How to test model's callback method independently?
提问by Billy Chan
I had a method in a model:
我在模型中有一个方法:
class Article < ActiveRecord::Base
def do_something
end
end
I also had a unit test for this method:
我也对这个方法进行了单元测试:
# spec/models/article_spec.rb
describe "#do_something" do
@article = FactoryGirl.create(:article)
it "should work as expected" do
@article.do_something
expect(@article).to have_something
end
# ...several other examples for different cases
end
Everything was fine until I found it's better to move this method into a after_savecallback:
一切都很好,直到我发现最好将此方法移动到after_save回调中:
class Article < ActiveRecord::Base
after_save :do_something
def do_something
end
end
Now all my tests about this method broken. I have to fix it by:
现在我关于这个方法的所有测试都失败了。我必须通过以下方式修复它:
- No more specific call to
do_somethingbecausecreateorsavewill trigger this method as well, or I'll meet duplicate db actions. - Change
createtobuild - Test respond_to
Use general
model.saveinstead of individual method callmodel.do_somethingdescribe "#do_something" do @article = FactoryGirl.build(:article) it "should work as expected" do expect{@article.save}.not_to raise_error expect(@article).to have_something expect(@article).to respond_to(:do_something) end end
- 不再对
do_something因为create或save将触发此方法进行更具体的调用,否则我将遇到重复的 db 操作。 - 更改
create为build - 测试 response_to
使用通用
model.save而不是单独的方法调用model.do_somethingdescribe "#do_something" do @article = FactoryGirl.build(:article) it "should work as expected" do expect{@article.save}.not_to raise_error expect(@article).to have_something expect(@article).to respond_to(:do_something) end end
The test passed but my concern is it's no longer about the specific method. The effect will be mixed with other callbacks if more added.
测试通过了,但我担心的是它不再与具体方法有关。如果添加更多,效果将与其他回调混合。
My question is, is there any beautiful way to test model's instance methods independentlythat becoming a callback?
我的问题是,有没有什么漂亮的方法可以独立地测试模型的实例方法来成为回调?
回答by Subhas
Callback and Callback behavior are independent tests. If you want to check an after_save callback, you need to think of it as two things:
回调和回调行为是独立的测试。如果要检查 after_save 回调,则需要将其视为两件事:
- Is the callback being fired for the right events?
- Is the called function doing the right thing?
- 回调是否为正确的事件触发?
- 被调用的函数是否在做正确的事情?
Assume you have the Articleclass with many callbacks, this is how you would test:
假设你的Article类有很多回调,这就是你测试的方式:
class Article < ActiveRecord::Base
after_save :do_something
after_destroy :do_something_else
...
end
it "triggers do_something on save" do
expect(@article).to receive(:do_something)
@article.save
end
it "triggers do_something_else on destroy" do
expect(@article).to receive(:do_something_else)
@article.destroy
end
it "#do_something should work as expected" do
# Actual tests for do_something method
end
This decouples your callbacks from behavior. For example, you could trigger the same callback method article.do_somethingwhen some other related object is updated, say like user.before_save { user.article.do_something }. This will accomodate all those.
这将您的回调与行为分离。例如,您可以在article.do_something更新其他相关对象时触发相同的回调方法,例如user.before_save { user.article.do_something }. 这将容纳所有这些。
So, keep testing your methods as usual. Worry about the callbacks separately.
所以,继续像往常一样测试你的方法。分别担心回调。
Edit: typos and potential misconceptions Edit: change "do something" to "trigger something"
编辑:错别字和潜在的误解 编辑:将“做某事”更改为“触发某事”
回答by Filip Bartuzi
You can use shoulda-callback-matchersto test existence of your callbacks without calling them.
您可以使用shoulda-callback-matchers来测试您的回调是否存在,而无需调用它们。
describe Article do
it { is_expected.to callback(:do_something).after(:save) }
end
If you also want to test the behaviour of the callback:
如果您还想测试回调的行为:
describe Article do
...
describe "#do_something" do
it "gives the article something" do
@article.save
expect(@article).to have_something
end
end
end
回答by bhfailor
In the spirit of Sandi Metz and minimalist testing, the suggestion in https://stackoverflow.com/a/16678194/2001785to confirm the call to a possibly private method does not seem right to me.
本着 Sandi Metz 和极简测试的精神,https://stackoverflow.com/a/16678194/2001785中确认调用可能是私有方法的建议对我来说似乎不正确。
Testing a publicly-observable side-effect or confirming an outgoing command message makes more sense to me. Christian Rolle provided an example at http://www.chrisrolle.com/en/blog/activerecord-callback-tests-with-rspec.
测试可公开观察到的副作用或确认传出的命令消息对我来说更有意义。Christian Rolle 在http://www.chrisrolle.com/en/blog/activerecord-callback-tests-with-rspec提供了一个例子。
回答by Teddy
This is more of a comment than an answer, but I put it here for the syntax highlighting...
这更多的是评论而不是答案,但我把它放在这里是为了突出显示语法......
I wanted a way to skip the callbacks in my tests, this is what I did. (This might help with the tests that broke.)
我想要一种在测试中跳过回调的方法,这就是我所做的。(这可能有助于失败的测试。)
class Article < ActiveRecord::Base
attr_accessor :save_without_callbacks
after_save :do_something
def do_something_in_db
unless self.save_without_callbacks
# do something here
end
end
end
# spec/models/article_spec.rb
describe Article do
context "after_save callback" do
[true,false].each do |save_without_callbacks|
context "with#{save_without_callbacks ? 'out' : nil} callbacks" do
let(:article) do
a = FactoryGirl.build(:article)
a.save_without_callbacks = save_without_callbacks
end
it do
if save_without_callbacks
# do something in db
else
# don't do something in db
end
end
end
end
end
end
回答by Ronak Taldar
describe "#do_something" do
it "gives the article something" do
@article = FactoryGirl.build(:article)
expect(@article).to have_something
@article.save
end
end

