2012-03-25 50 views
15

考慮下面的單元測試的例子。評論幾乎可以解釋我的問題。測試後總是清理乾淨?

[TestMethod] 
public void MyTestMethod() 
{ 

    //generate some objects in the database 
    ... 

    //make an assert that fails sometimes (for example purposes, this fails always) 
    Assert.IsTrue(false); 

    //TODO: how do we clean up the data generated in the database now that the test has ended here? 

} 
+0

您還應該使用'TearDown'(或其套件中的等價物)進行清理,因爲如果測試失敗,清理代碼將無法執行。 – 2012-03-26 00:39:45

+0

如何捕捉異常,然後在清理後重新拋出異常? – 2012-03-26 06:59:32

+0

Jimmy Bogard(* AutoMapper *的作者)有一篇很棒的文章和工具:[Respawn的可靠數據庫測試](https://lostechies.com/jimmybogard/2015/02/19/reliable-database-tests-with-respawn /) – 2016-06-14 10:32:41

回答

24

有兩種方法可以做到這一點。 One對測試類中的方法使用TestInitialize和TestCleanup屬性。他們將分別在測試前後分別運行。

另一種方法是使用事實,即測試失敗通過異常傳播到測試運行器。這意味着您的測試中的try {} finally {}塊可以在斷言失敗後用於清理任何事情。

[TestMethod] 
public void FooTest() 
{ 
    try 
    { 
    // setup some database objects 
    Foo foo = new Foo(); 
    Bar bar = new Bar(foo); 
    Assert.Fail(); 
    } 
    finally 
    { 
    // remove database objects. 
    } 
} 

嘗試/最後清理可以變得非常混亂是有很多的對象來清理。我的團隊傾向於實施IDisposable的助手類。它跟蹤已創建的對象並將它們推入堆棧。當調用Dispose時,項目從堆棧彈出並從數據庫中移除。

[TestMethod] 
public void FooTest() 
{ 
    using (FooBarDatabaseContext context = new FooBarDatabaseContext()) 
    { 
    // setup some db objects. 
    Foo foo = context.NewFoo(); 
    Bar bar = context.NewBar(foo); 
    Assert.Fail(); 
    } // calls dispose. deletes bar, then foo. 
} 

這有附加的好處,就是在方法調用中包裝構造函數。如果構造函數簽名更改,我們可以輕鬆修改測試代碼。

+9

我喜歡挑剔;如果TestInitialize引發異常,則TestCleanup不會運行。 – 2014-11-20 21:20:18

+4

@ carlin.scott你的挑逗只是挽救了我的一天 – 2015-07-02 12:22:48

6

一對夫婦的迴應:

  1. 如果它使用一個實際的數據庫,我認爲它不是在這個詞的嚴格意義上的「單元測試」。這是一個綜合測試。單元測試應該沒有這樣的副作用。考慮使用模擬庫來模擬實際的數據庫。 Rhino Mocks就是其中之一,但還有很多。

  2. 但是,如果此測試的整個實際上是與數據庫進行交互,那麼您需要與瞬態測試專用數據庫進行交互。在這種情況下,部分自動化測試將包含從頭構建測試數據庫的代碼,然後運行測試,然後銷燬測試數據庫。再次,這個想法是沒有外部副作用。可能有多種方法可以解決這個問題,並且我對單元測試框架不夠熟悉,無法給出具體的建議。但是,如果您使用Visual Studio內置的測試,那麼可能會使用Visual Studio Database Project

1

你的問題有點過於籠統。通常你應該在每一次測試後清理。通常你不能相信所有的測試總是以相同的順序執行,你必須確定數據庫中的內容。對於一般的設置或清理,大多數單元測試框架都提供了setUp和tearDown方法,您可以覆蓋並自動調用它們。我不知道它是如何在C#中工作的,但e。 G。在JUnit(Java)中你有這些方法。

我同意戴維。你的測試通常應該沒有副作用。您應該爲每個測試設置一個新的數據庫。

0

在這種情況下,您必須進行手動清理。也就是說,與在db中生成一些對象相反。

另一種方法,就是用嘲諷的工具,如犀牛嘲笑使數據庫只是一個內存數據庫

0

這取決於你是什麼實際上測試。看看評論,我會說,但順便說一下,很難扣除看評論。清理剛剛插入的對象,在實踐中重置測試的狀態。所以如果你清理,你開始從清理系統進行測試。

10

我認爲在這種情況下最好的答案是仔細考慮你想要測試的東西。理想情況下,單元測試應該試圖測試關於單一方法或函數的單一事實。當你開始把許多東西結合在一起時,它會跨越到集成測試的世界中(這同樣有價值,但卻不同)。

對於單元測試目的,爲了讓您只測試您想測試的東西,您將需要設計的可測試性。這通常需要額外使用接口(我假設你使用.NET展示的代碼)和某種形式的依賴注入(但是除非你需要,否則不需要IoC/DI容器)。它還受益於並鼓勵您在系統中創建非常緊密(單一目的)和分離(軟依賴性)類。

因此,當您測試依賴於來自數據庫的數據的業務邏輯時,通常會使用類似Repository Pattern的東西,並在單元測試中注入一個fake/stub/mock IXXXRepository。在測試具體存儲庫時,您需要執行所詢問的那種數據庫清理,或者需要填充/存根底層數據庫調用。這真的取決於你。

當您確實需要創建/填充/清理數據庫時,您可能會考慮利用大多數測試框架中提供的各種設置和拆卸方法。但要小心,因爲它們中的一些在每次測試之前和之後都會運行,這會嚴重影響單元測試的性能。運行速度太慢的測試不會經常運行,這很糟糕。

在MS-Test中,您用於聲明設置/拆卸的屬性爲ClassInitialize,ClassCleanUp,TestInitialize,TestCleanUp。其他框架也有相似的命名構造。

有一些框架,可以幫助你的嘲諷/磕碰的:MoqRhino MocksNMockTypeMockMoles and Stubs(VS2010),VS11 Fakes(VS11測試版)等,如果你正在尋找的依賴注入框架,看看Ninject,Unity,Castle Windsor

0

我認爲清理取決於您如何構建數據,因此如果「舊測試數據」不會與將來的測試運行交互,我認爲將它留下是很好的做法。

我在編寫集成測試時採取的一種方法是讓測試針對與應用程序數據庫不同的數據庫運行。我傾向於重建測試數據庫作爲每個測試運行的先決條件。這樣,每次測試都不需要一個粒度清理方案,因爲每次測試運行都會在運行之間得到清晰的結果。 我的大部分開發都是使用SQL服務器完成的,但在某些情況下,我已經針對SQL Compact版本數據庫運行測試,這對於在運行之間重建很快且高效。

0

mbUnit有一個非常方便的屬性回滾完成測試後清理數據庫。但是,您必須配置DTC(分佈式事務處理協調器)才能使用它。

0

我有一個類似的問題,其中一個測試的斷言是防止清理並導致其他測試失敗。

希望這對某些人有用。

[Test] 
    public void Collates_Blah_As_Blah() 
    { 
     Assert.False(SINGLETON.Collection.Any()); 

     for (int i = 0; i < 2; i++) 
      Assert.That(PROCESS(ValidRequest) == Status.Success); 

     try 
     { 
      Assert.AreEqual(1, SINGLETON.Collection.Count); 
     } 
     finally 
     { 
      SINGLETON.Collection.Clear(); 
     } 
    } 

finally塊會執行斷言是否通過或失敗,它也不會引入錯誤傳遞的風險 - 哪個catch會導致!