2009-01-26 17 views
4

我試圖通過將TDD應用到我的一個簡單項目來學習TDD。一些細節(和前面一個問題)在這裏:TDD:存根,模擬或以上都不是

TDD: Help with writing Testable Class

的細節是我有了個PurchaseOrder(在構造函數傳遞)的私有列表中PurchaseOrderCollection類,而個PurchaseOrders有一個布爾屬性的IsValid 。 PurchaseOrderCollection有一個屬性HasErrors,如果列表中的任何PurchaseOrders的IsValid爲false,則返回true。這是我想測試的邏輯。

[TestMethod] 
public void Purchase_Order_Collection_Has_Errors_Is_True_If_Any_Purchase_Order_Has_Is_Valid_False() 
{ 
    List<PurchaseOrder> orders = new List<PurchaseOrder>(); 

    orders.Add(new PurchaseOrder(--some values to generate IsValid false--)); 
    orders.Add(new PurchaseOrder(--some values to generate IsValid true--)); 

    PurchaseOrderCollection collection = new PurchaseOrderCollection(orders); 

    Assert.IsTrue(collection.HasErrors); 
} 

這與我以前在這個測試太耦合這個問題,我必須要知道的是什麼使了一個採購訂單的isValid或真或假,以通過測試的邏輯,如果真是這個測試不應該關心。問題不同(imo),因爲類本身不是問題。

本質上,我想能夠聲明一個具有IsValid假或真的PurchaseOrder,而不知道什麼是PurchaseOrder。

從我有限的TDD知識,這是你使用存根或模擬的東西。我的主要問題是,這是正確的?或者我應該使用不同的方法呢?還是我完全有缺陷,我只是寫這個測試,並認爲它錯了?

我最初的想法是隻使用某種模擬框架,並創建一個始終返回true或false的PurchaseOrder。從我讀過的內容來看,我需要聲明IsValid是虛擬的。所以我的第二個想法是將其改爲添加IPurchaseOrder作爲PurchaseOrder的接口,並創建一個總是返回false或true的假PurchaseOrder。這兩個有效的想法?

謝謝!

回答

6

您正處於正確的軌道上,存根或模擬。我更喜歡使用Mocking框架。

它將如何使用嘲笑的框架是,你會想嘲笑你的PurchaseOrder類,因此抽象它的實現。然後設置IsValid被調用的期望值以及何時調用返回此值。

使用 Moq例如,如果您正在使用C#3.0和.NET Framework 3.5

[TestMethod] 
public void Purchase_Order_Collection_Has_Errors_Is_True_If_Any_Purchase_Order_Has_Is_Valid_False() 
{  
    var mockFirstPurchaseOrder = new Mock<IPurchaseOrder>(); 
    var mockSecondPurchaseOrder = new Mock<IPurchaseOrder>(); 

    mockFirstPurchaseOrder.Expect(p => p.IsValid).Returns(false).AtMostOnce(); 
    mockSecondPurchaseOrder.Expect(p => p.IsValid).Returns(true).AtMostOnce(); 

    List<IPurchaseOrder> purchaseOrders = new List<IPurchaseOrder>(); 
    purchaseOrders.Add(mockFirstPurchaseOrder.Object); 
    purchaseOrders.Add(mockSecondPurchaseOrder.Object); 

    PurchaseOrderCollection collection = new PurchaseOrderCollection(orders); 

    Assert.IsTrue(collection.HasErrors); 
} 

編輯:
在這裏,我使用的界面來創建的PurchaseOrder的嘲笑,但是你沒有太。您可以將IsValid標記爲虛擬並模擬PurchaseOrder類。我的經驗法則是什麼時候應該首先使用虛擬。只是爲了創建一個接口,所以我可以在沒有任何架構原因的情況下模擬一個對象,這對我來說是一種代碼味道。

0

我不是單元測試專家,但這是我過去所做的。如果您有一個可以是有效/無效的PurchaseOder類,那麼我相信您也會爲這些類進行單元測試,以確定它們是否實際驗證。爲什麼不調用這些方法來生成有效和無效的PurchaseOrder對象,然後將其添加到您的集合中?

2

...本次測試中,我 太加上已經知道的是什麼造就了 PurchaseOrder的IsValid的虛假或真實 通過測試的時候,確實該測試 不應該關心的邏輯.. 。

其實我認爲相反 - 對於您的測試就知道有效性建模爲採購訂單中的布爾意味着你的測試知道太多的PurchaseOrder的實現(因爲它實際上PurchaseOrderCollection測試)。我沒有使用真實世界的知識(即有效或無效的實際值)來創建適當的測試對象的問題。最終,這就是你正在測試的內容(如果我給我的收藏訂購一個具有荒謬價值的訂單,它是否會正確地告訴我存在錯誤)。

一般來說,我儘量避免爲PurchaseOrder等「實體」對象編寫一個接口,除非除了測試之外還有其他原因(例如,生產中有多種PurchaseOrders並且接口是最好的方式來模擬)。

當測試表明您的生產代碼可以更好地設計時,這很棒。不過,如果要更改產品代碼只需即可進行測試。

好像我沒有寫足夠多,這裏有另一個建議 - 這是我真正在現實生活中解決這個問題的方法。

創建一個具有接口的PurchaseOrderValidityChecker。在設置isValid布爾值時使用它。現在創建一個有效性檢查器的測試版本,讓您指定要給出哪個答案。 (請注意,此解決方案可能還需要PurchaseOrderFactory或用於創建PurchaseOrders的等效項,以便在創建PurchaseOrderValidityChecker時可以爲每個採購訂單提供一個參考。)

+0

我也喜歡你的答案,但只能挑選一個〜 在這種情況下,構成有效採購訂單的邏輯增長超出了能夠通過單個採購訂單計算的邏輯。我還沒有編寫代碼,但將邏輯推出到PurchaseOrderValidityChecker似乎很可能。 – anonymous 2009-01-27 16:57:49

+0

我最初的這種猶豫是試圖在大型的無法測試的班級和班級爆發之間找到一個平衡點,在這個班級裏,我將每種方法都引入到自己的班級中。我想經驗在這裏會有所幫助。感謝您的回答。 – anonymous 2009-01-27 17:01:12

0

這兩個有效的想法?

是的。

您也可以創建一個可以返回有效和無效PurchaseOrders的Object Mother。

1

我最近問了一下關於測試的similar question。不要忘記:做最簡單的事情,然後在必要時重構。我個人試圖牢記更大的想法,但我也反對過度解決我的解決方案的衝動。您可以將兩個PurchaseOrder字段添加到您的測試類中,其中一個有效,另一個無效。使用這些字段將您的PurchaseOrderCollection置於您想要測試的狀態。你將需要學習如何最終模擬,但在這種情況下,當常規錘子解決問題時,你不需要大錘。通過使用模擬PurchaseOrder而不是具有所需狀態的具體PurchaseOrder,您不會獲得任何價值。

最重要的是,您通過測試PurchaseOrderCollection的行爲而不是僅測試PurchaseOrderCollection的狀態獲得更多。在您的測試確認PurchaseOrderCollection可以被置於其不同狀態之後,更重要的測試就是行爲測試。通過您認爲合適的方式將您的採購訂單集合變爲有效和無效狀態(嘲笑或更新所需狀態下的具體類),並測試PurchaseOrderCollection的每個狀態的邏輯是否正確執行,而不僅僅是PurchaseOrderCollection只是處於有效/無效狀態。

PurchaseOrderCollection將始終依賴於另一個類,因爲它是一個專門的集合。知道IPurchaseOrder具有IsValid屬性與知道具體的PurchaseOrder具有IsValid屬性沒有什麼不同。我會堅持最簡單的工作,例如一個具體的PurchaseOrder,除非你有很多理由相信你的系統中有多種類型的PurchaseOrders。那時,PurchaseOrder接口會更有意義。

1

我可以在這裏失去了一些背景,但在我看來,你必須您的示例的方式「夫婦」你的測試,否則你是不是真的什麼測試(除了一個IsValid屬性,這是微不足道的)。

嘲諷採購訂單的收益沒有 - 你測試模擬,沒有使用存根真正的類

- 同樣的事情

它是好的 - 如果不是強制性的 - 白箱測試時,使用TDD

1

首先,請記住您正在測試集合,而不是PurchaseOrder,因此這就是您的努力所在。這取決於多麼複雜PurchaseOrder是。如果它是一個具有明顯行爲的簡單實體,那麼創建實例可能是有意義的。如果它更復雜,那麼按照您所描述的方式提取界面是有意義的。

提出的下一個問題是該界面中的內容。對象在集合中需要扮演什麼角色?也許你只需要知道它們是否有效,在這種情況下,你可以提取IValidatable並縮小代碼中的依賴關係。在這種情況下,我不知道什麼是真實的,但我經常發現我可以使用接口將我推向更多專注的代碼。

相關問題