2

這是我與單元測試的第一次相遇,我想了解如何這個概念被一個簡單的日期驗證使用。第一單元測試(VS2010 C#)

用戶可以選擇表示直到可以進行支付日起TODATE。如果我們的日期無效,則不能付款。

private void CheckToDate(DateTime ToDate) 
    { 
     if (Manager.MaxToDate < ToDate.Year) 
      //show a custom message 
    } 

如何在這種情況下使用單元測試?

問候,

亞歷

謝謝您的回答:

如建議被很多的你,我會分裂的功能和驗證從消息顯示和使用單元測試,只爲這分開。

public bool IsDateValid(DateTime toDate) 
{ 
    return (Manager.MaxToDate < toDate.Year); 
} 

回答

5

是的,這是可能的。但是單元測試改變了你的班級的設計。爲了使這個代碼的可能單元測試,你應該做如下改變:

  1. 讓你的方法公開。 (可以使其受到保護,但爲了簡單起見將其公開)。

  2. 將此方法的所有外部依賴關係提取到接口,以便您可以嘲笑它們。然後,您可以使用一些模擬庫(moq,Rhino.Mocks)來模擬真正的依賴關係並寫入斷言。

  3. 寫測試。

下面是示例代碼。

類測試:

public class ClassUnderTest 
{ 
    public IManager Manager {get;set;} 
    public IMessanger Messanger {get;set} 

    public ClassUnderTest (IManager manager, IMessanger messanger) 
    { 
     Manager = manager; 
     Messanger = messanger; 
    } 

    private void CheckToDate(DateTime ToDate) 
    { 
     if (Manager.MaxToDate < ToDate.Year) 
      //show a custom message 
      Messanger.ShowMessage('message'); 
    } 
} 

測試:

[TestFixture] 
public class Tester 
{ 
    public void MessageIsShownWhenDateIsLowerThanMaxDate() 
    { 
     //SetUp 
     var manager = new Mock<IManager>(); 
     var messanger = new Mock<IMessanger>(); 

     var maxDate = DateTime.Now; 

     manager.Setup(m => m.MaxToDate).Returns(maxDate); 

     var cut = new ClassUnderTest (manager.Object, messanger.Object); 

     //Act 
     cut.CheckToDate(); 

     //Assert 
     messanger.Verify(foo => foo.ShowMessage("message"), Times.AtLeastOnce()) 
    } 
} 

設計的變化,通過測試介紹給你的系統nice脫鉤。如果外部依賴關係不是事件,則可以爲特定類編寫測試。

+0

+1 - 基本上相同的答案,但你把它完成得更快:)並得到了使用模擬庫卡在那裏。不錯的工作:) – 2011-02-15 08:54:48

2
:-)檢測時顯示可能需要一個小動作自定義消息

沒問題(我假設你的意思是在GUI上顯示一個消息框,但這個想法是一樣的,即使消息顯示不同) 。

不能從單元測試檢測mssage盒,無論是你想從你的單元測試啓動整個GUI環境。解決此問題的最簡單方法是將顯示消息框的實際代碼隱藏在單獨的方法中,理想情況是在不同的界面中。然後你可以爲你的單元測試注入一個這個接口的模擬實現。這個模擬不會顯示任何東西,只是記錄傳遞給它的消息,所以你可以在你的單元測試中檢查它。

的另一個問題是,你的方法是private。首先檢查它從哪裏調用,以及是否可以通過公共方法調用,而不會太複雜。如果不是,您可能需要(暫時)公開才能啓用單元測試。請注意,單元測試私有方法的需要通常是一種設計異味:您的課程可能會嘗試做太多,承擔太多不同的責任。您可能能夠將其某些功能提取到不同的類中,並在該類中公開,從而直接進行單元測試。但首先你需要進行單元測試,以確保重構時不會破壞任何東西。

然後,您需要在測試之前設置Manager.MaxToDate並帶有各種參數,並調用CheckToDate,檢查結果是否符合預期。

推薦閱讀類似的技巧,更是Working Effectively with Legacy Code

+0

感謝您的推薦閱讀;) – thedev 2011-02-15 08:59:36

2

單元測試你的類的公共接口上做得最好。所以,我建議你要麼公開,要麼間接測試它(通過你公開的方法)。

至於「是否可以爲這樣的事情創建單元測試?」,這取決於你想要在單元測試的概念上有多純粹,你希望它們如何依賴於用戶,以及究竟是什麼//show a custom message呢。

純度如何你想你的單元測試呢?如果你不在乎它們是否是骯髒的黑客,那麼你可以使用反射來將私有方法公開給你的單元測試,並直接調用它。但這通常是一種不好的做法,因爲您的私人功能在定義上可能會發生變化。否則,你只是把它們公諸於衆。

如果//show a custom message打印到控制檯,那麼你可以很容易使無聲運行測試。如果您確實想要驗證輸出,則必須掛接到您的Console.Out,以便您可以看到打印的內容並添加相應的斷言。

如果//show a custom message使用MessageBox.Show,那麼您可能必須進行UI自動測試才能測試此功能。您的測試將無法在後臺靜默運行,並且如果在測試運行時移動鼠標,將會中斷測試。

如果你不想做一個UI自動化測試只是爲了測試這個類的邏輯,我知道最好的辦法是修改你的類使用依賴注入。封裝所有實際輸出代碼(MessageBox.Show)的到另一個類,經由接口或抽象基類抽象它,並使其所以你的原始類需要的抽象類型的引用。這樣你可以在你的測試中注入一個模擬,並且它不會真正輸出到屏幕上。

public interface INotification 
{ 
    void ShowMessage(string message); 
} 

public class MessageBoxNotification : INotification 
{ 
    public void ShowMessage(string message) 
    { 
     MessageBox.Show(message); 
    } 
} 

public class MyClass 
{ 
    private INotification notification; 

    public MyClass(INotification notification) 
    { 
     this.notification = notification; 
    } 

    public void SomeFunction(int someValue) 
    { 
     // Replace with whatever your actual code is... 
     ToDate toDate = new SomeOtherClass().SomeOtherFunction(someValue); 
     CheckToDate(toDate); 
    } 

    private void CheckToDate(DateTime ToDate) 
    { 
     if (Manager.MaxToDate < ToDate.Year) 
      notification.Show("toDate, too late!: " + toDate.ToString()); 
    } 
} 

你的單元測試將讓自己的定製INotification類,它傳遞到的MyClass構造,並調用SomeFunction方法。

您可能會想要抽象像Manager這樣的東西,並且這些類以類似的方式涉及計算ToDate

1

介紹單元測試常常讓你覺得更加積極地瞭解代碼設計(如果你沒有的話)。你的情況在這方面很有趣。這是棘手的測試,並且其中的一個原因是,它兩個不同的東西:

  • 它驗證日期
  • 它通過展示好消息

一個在驗證失敗反應制作的方法只做一件事。所以,我會推薦重構一下代碼,這樣你就可以得到一個驗證方法,它除了驗證外什麼也不做。這種方法將是非常簡單的測試:

public bool IsDateValid(DateTime toDate) 
{ 
    // just guessing on the rules here... 
    return (Manager.MaxToDate >= toDate.Year); 
} 

這也將使得驗證代碼更可重複使用的,因爲它的動作如何,結果把給調用代碼desicion。