就像,我应该什么时候使用模拟/存根,什么时候不应该?
以这种情况为例:我有一个站点模型has_many:博客和博客模型has_many:文章.在我的站点模型中,我有一个回调过滤器,可以为每个新站点创建一组默认的博客和文章.我想测试那段代码,所以这里是:
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
现在,如果我运行该测试,一切都会过去.然而,它也很慢,因为它为每一次测试创建一个新网站,两个新博客和三个新文章!所以我想知道,这是否适合使用存根?我们试一试:
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
现在所有的测试仍然通过,事情也有点快.但是,我的测试时间增加了一倍,整个练习对我来说完全没有意义,因为我不再测试我的代码了,我只是测试我的测试.
现在,要么我完全错过了模拟/存根的点,要么我接近它根本就错了,但我希望有人能够:
>改进我上面的测试,以便它以实际测试我的代码的方式使用存根或模拟,而不是我的测试.
>或者,告诉我是否应该在这里使用存根 – 或者实际上这是完全没必要的,我应该将这些模型写入测试数据库.
解决方法
But,I’ve doubled the length of my tests and the whole exercise just strikes me as utterly pointless,because I’m no longer testing my code,I’m just testing my tests.
这是关键.不测试代码的测试没有用.如果您可以负面地更改您的测试应该测试的代码,并且测试不会失败,那么它们就不值得拥有.
根据经验,除非必须,否则我不喜欢模拟/存根.例如,当我正在编写控制器测试时,我想确保在记录无法保存时发生相应的操作,我发现更容易将对象的save方法存根以返回false,而不是仅仅仔细制作参数所以为了确保模型无法保存.
另一个例子是一个名为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
作为中间立场,您可能需要查看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) }