aar*_*ell 8 ruby unit-testing rspec ruby-on-rails mocking
我正在齐心协力将我的头围绕着Rspec,以便更多地转向TDD/BDD开发模式.然而,我还有很长的路要走,并且在一些基本面上挣扎:
就像,我应该什么时候使用模拟/存根,什么时候不应该?
以这种情况为例:我有一个Site模型has_many :blogs和Blog模型has_many :articles.在我的Site模型中,我有一个回调过滤器,为每个新站点创建一组默认的博客和文章.我想测试那段代码,所以这里是:
describe Site, "when created" do
include SiteSpecHelper
before(:each) do
@site = Site.create valid_site_attributes
end
it "should have 2 blogs" do
@site.should have(2).blogs
end
it "should have 1 main blog article" do
@site.blogs.find_by_slug("main").should have(1).articles
end
it "should have 2 secondary blog articles" do
@site.blogs.find_by_slug("secondary").should have(2).articles
end
end
Run Code Online (Sandbox Code Playgroud)
现在,如果我运行该测试,一切都会过去.然而,它也很慢,因为它为每一次测试创建一个新网站,两个新博客和三个新文章!所以我想知道,这是否适合使用存根?我们试一试:
describe Site, "when created" do
include SiteSpecHelper
before(:each) do
site = Site.new
@blog = Blog.new
@article = Article.new
Site.stub!(:create).and_return(site)
Blog.stub!(:create).and_return(@blog)
Article.stub!(:create).and_return(@article)
@site = Site.create valid_site_attributes
end
it "should have 2 blogs" do
@site.stub!(:blogs).and_return([@blog, @blog])
@site.should have(2).blogs
end
it "should have 1 main blog article" do
@blog.stub!(:articles).and_return([@article])
@site.stub_chain(:blogs, :find_by_slug).with("main").and_return(@blog)
@site.blogs.find_by_slug("main").should have(1).articles
end
it "should have 2 secondary blog articles" do
@blog.stub!(:articles).and_return([@article, @article])
@site.stub_chain(:blogs, :find_by_slug).with("secondary").and_return(@blog)
@site.blogs.find_by_slug("secondary").should have(2).articles
end
end
Run Code Online (Sandbox Code Playgroud)
现在所有的测试仍然通过,事情也有点快.但是,我的测试时间增加了一倍,整个练习对我来说完全毫无意义,因为我不再测试我的代码了,我只是测试我的测试.
现在,要么我完全错过了模拟/存根的点,要么我接近它根本就错了,但我希望有人能够:
但是,我将测试的长度增加了一倍,整个练习让我觉得完全毫无意义,因为我不再测试我的代码,我只是测试我的测试。
这就是关键。不测试代码的测试是没有用的。如果您可以对测试应该测试的代码进行负面更改,并且测试不会失败,那么它们就不值得拥有。
根据经验,除非必要,否则我不喜欢模拟/存根任何东西。例如,当我编写控制器测试时,我想确保在记录保存失败时执行适当的操作,我发现对对象的save方法进行存根以返回 false 更容易,而不是仔细设计参数以确保模型无法保存。
另一个例子是一个名为 helper 的函数admin?,它根据当前登录的用户是否是管理员返回 true 或 false。我不想伪造用户登录,所以我这样做了:
# helper
def admin?
unless current_user.nil?
return current_user.is_admin?
else
return false
end
end
# spec
describe "#admin?" do
it "should return false if no user is logged in" do
stubs(:current_user).returns(nil)
admin?.should be_false
end
it "should return false if the current user is not an admin" do
stubs(:current_user).returns(mock(:is_admin? => false))
admin?.should be_false
end
it "should return true if the current user is an admin" do
stubs(:current_user).returns(mock(:is_admin? => true))
admin?.should be_true
end
end
Run Code Online (Sandbox Code Playgroud)
作为中间立场,您可能需要研究Shoulda。通过这种方式,您可以确保您的模型定义了关联,并相信 Rails 已经经过充分测试,该关联将“正常工作”,而无需创建关联模型然后对其进行计数。
我有一个名为 的模型Member,基本上我的应用程序中的所有内容都与之相关。它定义了 10 个关联。我可以测试每个关联,或者我可以这样做:
it { should have_many(:achievements).through(:completed_achievements) }
it { should have_many(:attendees).dependent(:destroy) }
it { should have_many(:completed_achievements).dependent(:destroy) }
it { should have_many(:loots).dependent(:nullify) }
it { should have_one(:last_loot) }
it { should have_many(:punishments).dependent(:destroy) }
it { should have_many(:raids).through(:attendees) }
it { should belong_to(:rank) }
it { should belong_to(:user) }
it { should have_many(:wishlists).dependent(:destroy) }
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1275 次 |
| 最近记录: |