2010-09-13 66 views
8

爲了向更多TDD/BDD開發模式邁進,我正在齊心協力圍繞Rspec進行研究。然而,我還有很長一段路要走,並且在一些基本面上掙扎:何時以及何時不存根/模擬測試

就像我應該在什麼時候使用模擬/存根,什麼時候不應該?舉例來說,這種情況下:我有一個Site模型has_many :blogsBlog模型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 

現在,如果我運行該測試,一切都會通過。然而,它也很慢,因爲它會創建一個新網站,兩個新博客和三個新文章 - 每一個測試!所以我想知道,這是使用存根的好候選者嗎?讓我們一起去吧:

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 

現在所有的測試仍然通過,事情也有點快。但是,我的測試時間增加了一倍,整個練習讓我覺得完全毫無意義,因爲我不再測試我的代碼,我只是測試我的測試。現在

,或者我已經完全錯過了嘲笑/存根的點,或者我接近它根本上就是錯誤的,但我希望有人也許能夠之一:

  • 提高我上面的測試所以它以實際測試我的代碼的方式使用存根或模擬,而不是我的測試。
  • 或者,告訴我是否應該在這裏使用存根 - 或者實際上這是完全不必要的,我應該將這些模型寫入測試數據庫。

回答

2

但是,我已經翻了一番我的測試長度和整個演習只是令我毫無意義,因爲我不再是測試我的代碼,我只是在測試我的測試。

這是關鍵在這裏。不測試代碼的測試沒有用處。如果你可以負面地改變你的測試應該測試的代碼,並且測試不會失敗,那麼他們不值得擁有。作爲一個經驗法則,除非必須,否則我不喜歡模擬/殘缺任何東西。例如,當我正在編寫一個控制器測試時,我想確保在記錄無法保存時發生相應的操作,我發現將對象的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) } 
+0

謝謝,這是一個有用的迴應。:) – aaronrussell 2010-09-14 12:41:36

1

這就是爲什麼我使用存根/嘲笑很少(實際上只有當我將要觸及的外部Web服務)。節省的時間不值得增加複雜性。

有更好的方法來加快你的測試時間,Nick Gauthier給出了一個很好的談話,涵蓋了他們一堆 - 見videoslides

此外,我認爲一個好的選擇是嘗試一個內存中的sqlite數據庫爲您的測試運行。這應該可以減少數據庫時間,因爲不必爲所有內容都打滿磁盤。我自己並沒有嘗試過,但(我主要使用MongoDB,它具有相同的優點),所以請謹慎行事。 Here's是一個相當新的博客文章。

+0

謝謝爲您的迴應和鏈接。現在觀看視頻。 – aaronrussell 2010-09-14 12:42:30

1

我不太確定同意其他人。真正的問題(正如我看到的那樣)在於,您正在使用相同的測試(查找行爲和創建)測試多個有趣的行爲。爲什麼這是不好的原因,看看這個談話:http://www.infoq.com/presentations/integration-tests-scam。我假設這個答案的其餘部分是你想測試創建是你想測試的。

孤立症測試通常看起來很笨拙,但這往往是因爲他們有設計課程來教你。下面是我可以看到的一些基本的東西(雖然沒有看到生產代碼,我不能做太多好事)。

對於初學者來說,查詢設計,是否有Site添加文章到博客是否有意義? Blog上的類方法怎麼樣,叫做Blog.with_one_article。這就意味着你所要測試的是這個類的方法被調用了兩次(如果[據我現在的理解],你對每個Site都有一個「主」和「次」Blog,並且關聯建立了(我還沒有找到一個很好的方法來做到這一點在軌道上,但我通常不會測試它)

此外,你是否重寫ActiveRecord的創建方法,當你打電話Site.create?如果是這樣,我會建議做一個新的類方法在Site上命名爲別的東西(Site.with_default_blogs可能?)。這只是我的一個普通習慣,覆蓋東西通常會導致後續項目中出現問題。

相關問題