2014-09-24 53 views
2

我有一個類處理帳戶的東西。它提供了登錄,重置密碼和創建新賬戶的方法。TDD - 我做得對嗎?

我通過構造函數注入依賴關係。我有驗證每個依賴項的引用的測試,如果引用爲null,它會引發一個ArgumentNullException。

Account類通過只讀屬性公開每個這些依賴關係,然後我有測試來驗證在構造函數上傳遞的引用是否與屬性返回相同。我這樣做是爲了確保參考資料是由班級持有的。 (我不知道這是否也是一種好的做法。)

第一個問題:這是TDD的一個好習慣嗎?我問這個,因爲這個類到目前爲止有6個依賴關係,並且它非常重複,而且測試也很長,因爲我必須模擬每個測試的所有依賴關係。我所做的只是每次複製和粘貼,只需更改正在測試的依賴項的引用即可。第二個問題:我的賬戶創建方法執行如驗證通過的模型,將數據插入3個不同的表或第四個表(如果存在某組值併發送電子郵件)。我應該在這裏測試什麼?到目前爲止,我有一個測試,檢查模型驗證是否得到執行,如果調用每個存儲庫的Add方法,並且在這種情況下,我使用模擬存儲庫的Moq的回調方法來比較每個添加到存儲庫的屬性我通過模型傳遞的。

喜歡的東西:

userRepository 
     .Setup(r => r.Add(It.IsAny<User>())) 
     .Callback<User>(u => 
      { 
       Assert.AreEqual(model.Email, u.Email); 
       Assert.IsNotNull(u.PasswordHash); 
       //... 
      }) 
     .Verifiable(); 

正如我所說,這些測試越來越長,我認爲它不會傷害測試任何東西我可以,但我不知道這是值得的,因爲它編寫測試需要時間。

+2

「這是很好的TDD嗎?」真的是錯誤的問題;更有用的問題是「這是否適合我和我的項目?」像TDD這樣的方法論,儘管有什麼狂熱分子會讓你覺得這是一種達到目的的手段,而不是自己的目的。 – Casey 2014-09-24 03:57:48

+0

-1這不是一個TDD的問題,它是一個單元測試的設計問題。 – gurun 2014-09-26 13:58:28

+0

這是我的2美分。我不會將您的依賴作爲屬性公開。我看到了這種被開發者濫用的事情。在過去他們使用依賴關係的屬性暴露他們的類內的依賴關係(根本不漂亮)。如果組裝SUT類變得單調乏味,可以使用工廠方法創建SUT類,或者更好地使用測試生成器模式。 – Andrew 2014-09-26 20:39:09

回答

4

測試的目的是發現錯誤。

你真的會有一個屬性存在的錯誤,但沒有初始化爲構造函數的值嗎?另外,由於您使用的是TDD,因此您應該在編寫代碼之前編寫測試,並且代碼應該僅用於通過測試。而不是對屬性進行不必要的測試,編寫一個測試來證明正在使用注入的依賴關係。爲了或者這樣的測試通過,依賴關係需要存在,它需要是正確的類型,並且需要在特定的場景中使用。

在我的示例中,依賴關係將會因爲需要而存在,而不是因爲某些人工單元測試要求它存在。

1

通常在單元測試中(特別是TDD中的),您不會測試您正在測試的類中的每一條語句。 TDD單元測試的主要目的是測試該類的業務邏輯,而不是初始化的東西。

換句話說,您給出了場景(記住要包括邊緣案例)作爲輸入並檢查結果,結果可以是屬性的最終值和/或方法的返回值。

你不想在你的類中測試每一個可能的代碼路徑的原因是因爲如果你以後決定重構你的類,你只需要對你的TDD單元測試做最小的改變,因爲它們是應該對實際實施(儘可能)是不可知的。

注意:有其他類型的單元測試,如代碼覆蓋率測試,旨在測試您的類中的每個代碼路徑。但是,我個人發現這些測試不切實際,並且在TDD中肯定不受鼓勵。

3

你說寫這些測試感覺重複。我說你感覺TDD的主要好處。事實上,這並不是寫軟件而是編寫更好的軟件,而不是編寫更好的軟件,因爲TDD並不能保證(至少不是固有的)。 TDD迫使您考慮所有設計決策並做出設計決策。的。時間。 (並減少調試時間。)如果您在進行TDD時感到疼痛,通常是因爲設計決定回來咬你。接下來是切換到重構帽子並改進設計的時候了。

現在在這種特殊情況下,它只是您的測試設計,但您也必須爲這些設計做出設計決定。

至於測試屬性是否設置。如果我正確地理解了你,你只是爲了測試而暴露這些屬性?在那種情況下,我建議不要這樣做。假設你有一個構造函數參數的類,有一個測試斷言construtor應該扔在空參數:

public class MyClass 
{ 
    public MyClass(MyDependency dependency) 
    { 
     if (dependency == null) 
     { 
      throw new ArgumentNullException("dependency"); 
     } 
    } 
} 

[Test] 
public void ConstructorShouldThrowOnNullArgument() 
{ 
    Assert.Catch<ArgumentNullException>(() => new MyClass(null)); 
} 

(的TestFixture類略)

現在,當你開始寫一個測試的實際被測班的業務方法,各部分將開始融合在一起。

[Test] 
public void TestSomeBusinessFunctionality() 
{ 
    MyDependency mockedDependency; 

    // setup mock 
    // mock calls on mockedDependency 

    MyClass myClass = new MyClass(mockedDependency); 

    var result = myClass.DoSomethingOrOther(); 

    // assertions on result 
    // if necessary assertion on calls on mockedDependency 
} 

在這一點上,你將不得不從構造函數注入依賴分配給字段,所以你可以在方法用到它。如果你設法讓測試通過而不使用依賴......呃,呃,顯然你並不需要它開始。或者,也許,你只會開始需要它進行下一個測試。

關於其他問題。當測試方法或類的所有權限變得麻煩時,TDD告訴你方法/類正在做很多事情,並且可能想分解成容易測試的部分。例如。一個用於驗證,一個用於映射,一個用於執行存儲調用。

雖然這可能會導致過度工程!所以要留意這一點,你會開始感覺什麼時候拒絕更加間接的衝動。 ;)

爲了測試屬性是否正確映射,我建議使用存根或自制的具有簡單屬性的假對象。這樣你可以簡單地比較源和目標屬性,不必像發佈的那樣進行冗長的設置。