2011-09-13 97 views
2

我聽說某個地方嘲笑一個實體是一件壞事,你應該只嘲笑服務(即使我們沒有做全面的DDD,但是「有點」DDD)。嘲笑一個實體?

因此,鑑於以下典型故事,您如何爲它編寫測試?

  • 如果客戶具有「首選」狀態,則應在事件發生時通知她。

優先地位是值得依賴於其他的東西,比如,客戶最近購買了10只小工具,或他的名字與「A」等開始無論如何,讓我們假設我們已經實現了這個功能。我如何爲上述故事撰寫測試?具體來說,我對這種情況下可測試性要求會如何影響我的設計感興趣。

  1. 我可以使用一些先進的模擬框架(如隔離器),它讓我模擬非虛擬屬性。測試性和設計沒有關聯,沒有問題。
  2. 我可以使IsPreferred屬性虛擬和模擬它(實際上是存根)。不知道爲什麼,但感覺很髒。查看帖子頂部的問題。另外,不知道它如何改進我的設計。 2a。隱藏ICustomer界面背後的實體並對其進行嘲弄。完全不冷。
  3. 我可以讓它成爲一個可讀寫的屬性,但這將是一個非常糟糕的設計決定。

你如何處理這樣的故事?

+0

「我聽說過某處」 - >在哪裏? –

+0

當前設置的首選狀態如何? –

+0

@丹尼爾不記得了,也許在我的想象中。 – ulu

回答

2

我不會把太多邏輯變成實體。他們應該關心自己的國家,例如。關心自己領域的一致性。但他們不應該關心整個系統的一致性。你設置用戶狀態的「user.AddOrder」的例子實在太過分了。

如果他們做了所有事情,那麼您也會失去實體的可重用性。如果您獲得額外的要求(例如數據導入,新的訂單類型,新的規則等額外情況),您的實體模型會妨礙您的實施,因此不夠靈活。

缺乏靈活性在單元測試中出現。當你在單元測試中隔離類時,它會清楚實際封裝的內容,不能再分開。如果你遇到問題,你的封裝很可能不是很有用。所以單元測試是可重用性的一個很好的證明。

例(僞代碼,不介意我想念你的應用程序的角度來看,它只是表明其中的邏輯屬於):

class User 
{ 
    AccountState State { get; } 
    void MakeTopSeller() 
} 

class Order 
{ 
    decimal GetTotal(); 
    User Salesman { get; } 
} 


class OrderService 
{ 
    void AddOrder() 
    { 
     // .... 
     if (order.GetTotal() > ToSellerAmount) 
     { 
      order.Salesman.MakeTopSeller(); 
     } 
    } 
} 

編輯:如果你仍然認爲你的方法是合適的(我的意思是我不能根據我所知道的來決定),你不需要改變它。但是如果你的實體需要另一個實體來設置一個狀態,並且你在測試中需要這個狀態,那麼你需要在測試中做所有這些事情:創建一個訂單並將其添加到用戶中。

還有另一種分解方法。你可以把規則放到一個單獨的類中,例如。使用戰略模式。通常很難在實體上設置策略,因爲底層數據庫層需要注入它們。

6

我在幾本書/博客上看過類似的東西,建議不要嘲笑域模型中的實體或對象。據我記得消息是不要模擬數據,模擬行爲。 (我可能在本書的XUnit Test Patterns或GOOS中讀到過)。

就我個人而言,我認爲如果您的實體需要很多設置,它是有效的嘲笑它。讓我舉一個例子,假設你有品牌,產品,價格和StockAvailability。品牌有產品,產品有價格和庫存。 Class Under Test調用Brand.IsAvailable(),它依次檢查至少有一個產品的有效價格和庫存(這是相當多的邏輯)。因此,如果您嘗試對CUT進行單元測試,那麼測試所有Brand.IsAvailable邏輯已超出範圍,因此嘲笑該方法是有意義的。

如果它很容易成立的實體,那麼使用實體,例如,如果你有一個用戶,它有一個叫active屬性,你可以創建一個用戶,並設置該屬性,在測試作爲成本建立一個假的用戶和分配這個屬性可能與嘲笑方法調用相同,並且(在我看來)更清晰。

我不是c#專家,但我讀過隔離器是.net最好的框架之一,所以我會做你在上提到的。

+0

你有一個有效的觀點。我唯一的反對意見是IsPreferred屬性是業務邏輯的一部分,可能會改變。所以,如果我爲一個規則設置一個實體,並且它更改爲另一個規則,那麼我的測試顯示用戶沒有得到通知,而實際上他是。例如,如果所有名爲Alex的用戶都是首選的,那麼在測試中很容易設置。將此更改爲Brad,並且在所有Brads都得到正確通知後,測試仍然使用Alex並發現他不再被通知。 – ulu