簡體   English   中英

什么時候以及什么時候不存根/模擬

[英]When and when not to stub/mock a test

我正在齊心協力圍繞Rspec努力,以朝着更多的TDD / BDD開發模式邁進。 但是,我還有很長一段路要走,並且在一些基本原則上苦苦掙扎:

就像,什么時候應該使用模擬/存根,什么時候不應該使用?

以這種情況為例:我有一個has_many :blogsSite模型,而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

現在,如果我運行該測試,一切都會通過。 但是,它也很慢,因為它為每個測試都創建了一個新站點,兩個新Blog和三個新文章! 所以我想知道,這是使用存根的好候選人嗎? 讓我們開始吧:

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

現在,所有測試仍然通過,而且速度也有所提高。 但是,我的測試時間增加了一倍,而整個練習卻毫無意義,因為我不再測試代碼,而只是測試了。

現在,或者我完全錯過了模擬/存根的要點,或者我從根本上解決了這個問題,但是我希望有人能夠做到:

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

但是,我的測試時間增加了一倍,而整個練習卻毫無意義,因為我不再測試代碼,而只是測試了。

這就是這里的關鍵。 不測試代碼的測試沒有用。 如果您可以否定地更改您應該測試的代碼,並且測試不會失敗,則不值得擁有。

根據經驗,除非有必要,否則我不喜歡模擬/存根任何內容。 例如,當我編寫控制器測試時,我想確保在記錄保存失敗時發生適當的動作,我發現將對象的save方法存根以返回false更容易,而不是僅僅精心設計參數因此,為了確保模型無法保存。

另一個示例是一個名為admin?的助手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) }

這就是為什么我很少使用存根/模擬的原因(僅當我要使用外部Web服務時才使用)。 節省的時間不值得增加額外的復雜性。

有更好的方法可以加快測試時間,Nick Gauthier很好地介紹了很多方法,請參見視頻幻燈片

另外,我認為一個不錯的選擇是嘗試使用內存中的sqlite數據庫進行測試。 這樣就不必在磁盤上存放所有東西,從而可以大大減少數據庫時間。 不過,我自己還沒有嘗試過(我主要使用MongoDB,它具有相同的好處),因此請謹慎操作。 這是一篇有關它的近期博客文章。

我不太同意其他人。 真正的問題(如我所見)是,您正在使用相同的測試(發現行為和創建)測試多個有趣的行為。 有關導致此問題的原因的詳細信息,請參見此討論: http : //www.infoq.com/presentations/integration-tests-scam 對於此答案的其余部分,我假設您要測試創建的內容就是您要測試的內容。

隔離測試似乎很笨拙,但這通常是因為它們有設計課程來教您。 以下是我可以從中看到的一些基本內容(盡管沒有看到生產代碼,但是我做不到太多的事情)。

首先,要查詢設計,讓Site向博客添加文章是否有意義? Blog上的類方法Blog.with_one_articleBlog.with_one_article 然后,這意味着您需要測試的是該類方法已被調用兩次(如果[據我目前所知],則每個Site都有一個“主” Blog和“輔助” Blog ,並且已經建立了關聯(我還沒有找到在Rails中執行此操作的好方法,我通常不對其進行測試)。

此外,您在調用Site.create時是否覆蓋ActiveRecord的create方法? 如果是這樣,我建議在Site上創建一個新的類方法,命名為其他名稱(可能是Site.with_default_blogs ?)。 這只是我的一個普遍習慣,過多的東西通常會在以后的項目中引起問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM