2012-06-04 27 views
2

我是單元測試的新手 - 我只使用純粹的Testmethods(我的最後一個模塊,我創建了其中的約50個)完成了基本的斷言測試。瞭解一些單元測試的做法

我正在讀單元測試一本書,書中的衆多例子中的一個已經創造了我對每個單獨測試一個新的類。以下是爲一個測試用例創建的示例對象之一。我的問題是有沒有必要這樣做?或者什麼時候應用這種方法,何時不需要?

public class and_saving_an_invalid_item_type : when_working_with_the_item_type_repository 
    { 
     private Exception _result; 

     protected override void Establish_context() 
     { 
      base.Establish_context(); 

      _session.Setup(s => s.Save(null)).Throws(new ArgumentNullException()); 
     } 

     protected override void Because_of() 
     { 
      try 
      { 
       _itemTypeRepository.Save(null); 
      } 
      catch (Exception exception) 
      { 
       _result = exception; 
      } 
     } 

     [Test] 
     public void then_an_argument_null_exception_should_be_raised() 
     { 
      _result.ShouldBeInstanceOfType(typeof(ArgumentNullException)); 
     } 
    } 

回答

3

您是否需要爲每個單獨的測試創建一個新類?我會說不,你當然不會。我不知道這本書爲什麼這樣說,或者他們只是在幫助說明他們的例子。

爲了回答你的問題,我建議每個組的測試中使用一個類...但它確實比這更復雜一點,因爲你如何定義「組」是多種多樣的,取決於你當時正在做。

在我的經驗,一組測試真的邏輯結構類似於一個文件,這可以通過一些常用的方面包含的測試中,分組(和有時嵌套的)一個或多個集合在一起。用於測試面向對象代碼的自然分組是按類別分組,然後按方法分組。

下面是一個例子

  • 試驗類別1個
    • 試驗方法1次
        方法的
      • 初級行爲1次
      • 方法的
      • 替代行爲1
    • 測試爲方法2
      • 方法的主要行爲2
      • 方法的替代行爲2

不幸的是,在C#或Java(或類似的語言),你只得到了結構的兩個水平(而不是你真正想要的3或4),所以你必須破解適合的東西。

做到這一點的常用方法是使用一類組合到一起組測試,並沒有任何團體在方法層面,因爲像這樣的:

class TestsForClass1 { 
    void Test_method1_primary() 
    void Test_method1_alternate() 

    void Test_method2_primary() 
    void Test_method2_alternate() 
} 

如果同時你的方法1方法2都具有相同的安裝/拆卸,那麼這是好的,但有時他們不這樣做,導致這種故障:

class TestsForClass1_method1 { 
    void Test_primary() 
    void Test_alternate() 
} 

class TestsForClass1_method2 { 
    void Test_primary() 
    void Test_alternate() 
} 

如果你有更復雜的需求(假設你有method_1 10次的測試中,前5個具有設置要求X,接下來的5個具有不同的設置要求),那麼人們通常最終只是使得越來越多的類名是這樣的:

class TestsForClass1_method1_withRequirementX { ... } 
class TestsForClass1_method1_withRequirementY { ... } 

這很爛,但嘿 - 方掛,圓孔等

個人而言,我的粉絲在方法內部使用lambda函數爲您提供第三級分組。 NSpec顯示了一種方式,可以做到這一點......我們有一個內部測試框架,它是略有不同,它讀取有點像這樣:

class TestsForClass1 { 
    void TestsForMethod1() { 
     It.Should("perform it's primary function",() => { 
     // .... 
     }); 

     It.Should("perform it's alternate function",() => { 
     // .... 
     }); 
    } 
} 

這有一些缺點(如果第一個它語句失敗,別人不跑),但我認爲這種折衷值得)


- 問題最初讀:「是它曾經真的有必要創建一個對象的每個單獨的測試,我想繼續出來嗎?」。根據這個解釋,答案是(主要)是。

通常,單元測試涉及的兩個部分

  • 被測對象的相互作用。通常這是你寫的一個類或一個函數的實例
  • 環境。通常這是你傳遞給函數的任何參數,以及對象可能引用的其他依賴關係。

爲了使單元測試可靠,這兩個部分都需要對每個測試都是「新鮮」的,以確保系統的狀態合理可靠。

  • 如果被測的事情是不會刷新爲每個測試,然後一個功能可以改變對象的內部狀態,並導致下一個測試錯誤地失敗

  • 如果環境不刷新每個測試,那麼一個函數可能會改變環境(例如:在外部數據庫或其他地方設置一些變量),這可能會導致下一次測試錯誤地失敗。

很顯然,許多情況下,這是不是這樣的 - 你可能例如有一個純粹的數學函數只需要整數作爲參數,並且不觸及任何外部狀態,那麼你可能不希望麻煩重新創建被測對象或測試環境......但通常,任何面向對象系統中的大多數事情都需要更新,所以這就是爲什麼這是「標準實踐」。

2

我不太能夠效仿你的例子,但理想情況下,任何測試用例都應該能夠獨立於任何其他運行 - 獨立於其他任何東西真的。