2009-08-03 216 views
8

假設你有一個方法:單元測試(是或否)

public void Save(Entity data) 
{ 
    this.repositoryIocInstance.EntitySave(data); 
} 

你寫單元測試呢?

public void TestSave() 
{ 
    // arrange 
    Mock<EntityRepository> repo = new Mock<EntityRepository>(); 
    repo.Setup(m => m.EntitySave(It.IsAny<Entity>()); 

    // act 
    MyClass c = new MyClass(repo.Object); 
    c.Save(new Entity()); 

    // assert 
    repo.Verify(m => EntitySave(It.IsAny<Entity>()), Times.Once()); 
} 

因爲以後如果要更改方法的實現做更多的「複雜」的東西,如:

public void Save(Entity data) 
{ 
    if (this.repositoryIocInstance.Exists(data)) 
    { 
     this.repositoryIocInstance.Update(data); 
    } 
    else 
    { 
     this.repositoryIocInstance.Create(data); 
    } 
} 

...單元測試會失敗,但它可能不會打破你的應用程序...

問題

應我甚至懶得上的方法創建單元測試,沒有任何返回類型*或**不改變內部模擬以外的任何內容?

回答

2

這是真的,你的測試取決於你的實現,這是你應該避免的東西(雖然它有時候並不是那麼簡單),並不一定是壞的。 但是,如果您的更改不會破壞代碼,這些測試預計會中斷。

你可以有很多方法是:按預期

  • 創建一個測試,真正進入到數據庫並檢查是否狀態改變(這不會單元測試了)
  • 創建一個僞造數據庫並在內存中執行操作(針對repositoryIocInstance的另一個實現)的測試對象,並驗證狀態是否按預期更改。更改存儲庫接口也會導致更改此對象。但是你的界面不應該改變太多,對吧?
  • 參見本由於價格太貴一切,用你的方法,這可能會產生對後期不必要的開斷試驗(但一旦機會是低,它是確定承擔的風險)
1

一般的經驗法則是,你測試所有的東西,可能會打破。如果您確定,該方法足夠簡單(並且保持簡單)不會成爲問題,因此可以通過測試將其解決。

第二件事,你應該測試方法的合同,而不是實施。如果測試在更改後失敗,但不是應用程序,那麼測試測試不是正確的。測試應涵蓋對您的應用程序很重要的案例。這應該確保,對不中斷應用程序的方法的每次更改都不會使測試失敗。

+0

在這種情況下,此方法不需要單元測試,因爲不會中斷應用程序,測試會中斷。在這種情況下,我正在測試imeplementation,而不是在方法範圍內不存在的合同。 – 2009-08-03 10:34:17

1

不返回任何結果的方法仍會改變應用程序的狀態。在這種情況下,您的單元測試應該測試新狀態是否符合預期。

+0

單元測試中的新狀態完全取決於模擬......這就是爲什麼我有第二個想法。如果我所有的方法確實存在於知識庫中,並且在測試中被模擬,那麼它就會基於模擬而不是真實的生活情況產生可預測的結果。它會一直通過。 – 2009-08-03 10:32:41

+0

你不應該測試模擬,而是測試與模擬交互的操作。比方說,你有一個調用庫的方法爲`M()`的對象`X`。你想測試一下調用存儲庫的方式,所以你編寫了一個模型庫,然後調用你的`X.M()`方法。測試應該觸發模擬中的變化,這將證明你的方法`M()`按需要工作。當你有一個真正的存儲庫實現,然後你測試該實現(而不是模擬) - 然後你將測試存儲庫。 – 2009-08-03 11:35:19

+0

@jeyoung:但這是一個單獨的單元測試。這是對存儲庫的單元測試,而不是根據上面的代碼調用存儲庫的類...但是。事情是我不測試DAL,因爲它是生成代碼。並期望按照它應該的工作。我不打算單元測試它。 – 2009-08-03 14:14:01

1

「單元測試會失敗,但它可能不會打破你的應用程序」

這是 - 實際上 - 真正重要的是要知道的。這看起來可能很煩人,但是當別人開始維護你的代碼時,他們可能會對Save和(不太可能)破壞應用程序做出非常糟糕的改變。

訣竅是優先。

首先測試重要的東西。當事情緩慢時,爲微不足道的東西添加測試。

+0

您可能在最後一句話中是正確的(對此爲+1)。這裏的主要問題是測試會變得無效,而不是代碼,這將演變成更好的版本。 – 2009-08-12 14:20:35

6

不要忘記,單元測試不僅僅是測試代碼。這是關於允許您在行爲發生變化時確定

所以你可能有一些微不足道的東西。但是,您的實施更改,您可能會有副作用。你想讓你的迴歸測試套件告訴你。

例如通常人們說你不應該測試setter/getters,因爲它們很瑣碎。我不同意,不是因爲他們是複雜的方法,而是有人可能會因疏忽,胖手指等情況而無意中改變他們。

鑑於我剛纔所說的,我肯定會對上述內容進行測試(通過嘲諷,並且/或者值得設計你的課程,考慮到可測試性並讓他們報告狀態等。)

+0

我同意你對迴歸測試的看法。而且我也會說集成測試也會顯示一些非工作部分。但由於我沒有爲我的DAL創建單元測試(它生成),我想我不應該創建單元測試,除非我改變我的方法有一些返回類型(如布爾)來報告狀態。 – 2009-08-03 10:37:14

1

當有ISN」在一個方法中斷言,你基本上斷言異常不會被拋出。

我也在努力解決如何測試public void myMethod()的問題。我想如果你決定添加一個可測試性的返回值,那麼返回值應該代表所有必要的重要事實,以查看應用程序狀態的變化。

public void myMethod() 

成爲

public ComplexObject myMethod() { 
DoLotsOfSideEffects() 
return new ComplexObject { rows changed, primary key, value of each column, etc }; 
} 

,而不是

public bool myMethod() 
    DoLotsOfSideEffects() 
    return true; 
2

簡短的回答你的問題是:,你一定要測試方法類似。

我假設它是重要那Save方法實際上是保存了的數據。如果你不爲此編寫單元測試,那麼你怎麼知道?

其他人可能會出現並刪除調用EntitySave方法的代碼行,並且任何單元測試都不會失敗。後來,你想知道爲什麼項目永遠不會持續......

在你的方法,你可以說任何人刪除該行只會這樣做,如果他們有惡意的意圖,但事情是:簡單的事情不'不一定要保持簡單,你最好在事情變得複雜之前編寫單元測試。

它是而不是 Save方法在Repository上調用EntitySave的實現細節 - 它是預期行爲的一部分,也是非常關鍵的部分,如果我可以這樣說的話。你想確保數據實際上被保存。

僅僅因爲一個方法沒有返回值並不意味着它不值得測試。一般來說,如果您觀察到良好的命令/查詢分離(CQS),則預計任何無效方法都會更改的某些內容

有時候某些東西是類本身,但有時候,它可能是其他東西的狀態。在這種情況下,它改變了Repository的狀態,這就是你應該測試的。

這就是所謂的測試Inderect輸出,而不是更正常直接輸出(返回值)。

訣竅是編寫單元測試,以防止它們經常被打破。當使用Mocks時,很容易意外地寫出指定測試,這就是爲什麼大多數動態模擬(如Moq)默認爲存根模式,其中調用給定方法的次數無關緊要。

所有這一切,以及更多,在優秀的xUnit Test Patterns解釋。

3

問問你自己兩個問題。 「這個單元測試的手冊相當於什麼?」和「是否值得自動化?」。在你的情況下,它會是這樣的:

什麼是手動等效? - 啓動調試器 - 步入「保存」方法 - 步到明年,確保你IRepository.EntitySave裏面執行

是否值得自動化?我的答案是「不」。代碼是100%顯而易見的。 從數百個類似的廢物測試中,我沒有看到一個會變得有用的單個測試。

相關問題