2014-04-01 73 views
8

我正在爲我的控制器和服務層(C#,MVC)進行單元測試。我使用Moq dll來模擬單元測試中的真實/依賴對象。嘲笑單元測試中的依賴有什麼好處?

但我對嘲笑依賴關係或真實對象有點混淆。讓我們下面單元測試方法的一個例子: -

[TestMethod] 
public void ShouldReturnDtosWhenCustomersFound_GetCustomers() 
{ 
    // Arrrange 
    var name = "ricky"; 
    var description = "this is the test"; 

    // setup mocked dal to return list of customers 
    // when name and description passed to GetCustomers method 
    _customerDalMock.Setup(d => d.GetCustomers(name, description)).Returns(_customerList); 

    // Act 
    List<CustomerDto> actual = _CustomerService.GetCustomers(name, description); 

    // Assert 
    Assert.IsNotNull(actual); 
    Assert.IsTrue(actual.Any()); 

    // verify all setups of mocked dal were called by service 
    _customerDalMock.VerifyAll(); 
} 

在上述單元測試方法,我嘲笑GetCustomers的方法並返回一個客戶列表。哪些已經定義。看起來像如下:

List<Customer> _customerList = new List<Customer> 
{ 
    new Customer { CustomerID = 1, Name="Mariya",Description="description"}, 
    new Customer { CustomerID = 2, Name="Soniya",Description="des"}, 
    new Customer { CustomerID = 3, Name="Bill",Description="my desc"}, 
    new Customer { CustomerID = 4, Name="jay",Description="test"}, 
}; 

,並讓對客戶嘲笑對象和實際對象斷言的斷言一看: -

Assert.AreEqual(_customer.CustomerID, actual.CustomerID); 
Assert.AreEqual(_customer.Name, actual.Name); 
Assert.AreEqual(_customer.Description, actual.Description); 

但在這裏我不理解這(上述單元測試)總是工作正常。意思是我們只是測試(在斷言中),我們通過了或者我們正在返回(在嘲笑對象中)。而且我們知道真實/實際對象將始終返回我們傳遞的列表或對象。

那麼在這裏做單元測試或嘲諷有什麼意義?

回答

5

嘲笑的真正目的是實現真正的隔離。

假設你有一個CustomerService類,這取決於CustomerRepository。你寫幾個單元測試,涵蓋CustomerService提供的功能。他們都通過了。

一個月後,做了一些更改,突然你的CustomerServices單元測試開始失敗 - 並且你需要找到問題出在哪裏。

所以你認爲:

因爲它測試CustomerServices失敗單元測試,這個問題必須在該類!

對不對?錯誤!問題可能在CustomerServices或其任何依賴關係中,即CustomerRepository。如果它的任何依賴失敗,被測試的機會很可能會失敗。

現在想象一個巨大的依賴關係鏈:A取決於BB取決於C ... Y取決於Z。如果在Z,全部中引入故障,則您的單元測試將失敗。

這就是爲什麼你需要的類項下的其依賴性測試隔離(它可能是一個域對象,數據庫連接,文件資源等)。你想測試一個單元

+0

其清除所有的事情。 – Pawan

1

你的例子太簡單了,不能展示嘲笑的真正好處。那是因爲你的邏輯在測試中並沒有超出返回一些數據的功能。

但想象一下你的邏輯做了一些基於掛鐘時間的例子,比如每小時安排一些過程。在這樣的情況下,嘲笑時間源可以讓你實際測試這種邏輯單元,這樣你的測試就不用跑幾個小時,等待時間過去了。

0

在這種情況下,很嘲諷允許你僞造一個數據庫連接,這樣就可以到位,並在內存中運行測試,而無需依賴任何額外的資源,即數據庫。這個測試聲明,當一個服務被調用時,調用一個相應的DAL方法。

但是後來斷言名單並在列表中的值是沒有必要的。正如你正確地注意到你只是斷言你「嘲笑」的值被返回。這在嘲笑框架本身中很有用,可以斷言嘲笑方法的行爲如預期。但是在你的代碼中只是多餘的。

在一般的情況下,嘲諷允許一個:

  • 測試行爲(有事時,則執行特定的方法)
  • 假的資源(例如,電子郵件服務器,Web服務器,HTTP API請求/響應,數據庫)

相比之下,沒有模擬的單元測試通常允許您測試狀態。也就是說,當一個特定的方法被調用時,你可以檢測對象狀態的變化。

1

除了已經說過的內容之外:

我們可以有沒有依賴關係的類。我們唯一擁有的是沒有模擬和存根的單元測試。

當我們依賴有幾種人:我們班大都採用的「發射後不管」的方式

  • 服務,即服務,不影響消費代碼的控制流。

我們可以嘲笑這些(和所有其他類型)的服務,以測試他們正確調用(集成測試)或只是爲了注射,因爲他們可以通過我們的代碼是必需的。

  • 雙向服務提供的結果,但沒有一個內部 狀態,不影響系統的狀態。他們可以被稱爲複雜的數據轉換。

通過嘲笑這些服務,您可以測試您對不同變體的服務實現的代碼行爲的期望,而無需保留所有這些服務。

  • 服務從而影響系統的狀態或依賴於真實世界 現象或東西在你的控制。 '@ 500 - 內部服務器錯誤'給出了時間服務的一個很好的例子。

嘲笑你可以讓時間以任何需要的速度(和方向)流動。
另一個例子是使用數據庫。當單元測試通常不希望改變數據庫狀態時,功能測試是不正確的。對於這種服務,「隔離」是嘲笑的主要(但不是唯一)動機。

  • 您的代碼依賴於內部狀態的服務。

考慮實體框架:
SaveChanges()被調用時,許多事情發生在幕後。 EF檢測更改和修復導航屬性。此外,EF不會允許您使用相同的密鑰添加多個實體。
顯然,嘲笑這種依賴關係的行爲和複雜性是非常困難的......但是如果它們設計得很好,通常就不會這樣做。如果您嚴重依賴某些組件提供的功能,您幾乎無法取代這種依賴關係。可能需要的是隔離。測試時你不想留下痕跡,因此黃油方法是告訴EF不要使用真實的數據庫。是的,依賴意味着不僅僅是一個界面。更常見的不是方法簽名,而是預期行爲的合同。例如IDbConnectionOpen()Close()方法暗示某些調用序列。

當然,這不是嚴格的分類。最好把它當作極端。

@dcastro寫道:You want to test a unit.然而,聲明並沒有回答你是否應該這樣的問題。
讓我們不打折集成測試。有時候知道系統的某些複合部分出現故障是可以的。
對於@dcastro給出的依賴關係鏈的示例,我們可以嘗試找到袋子可能存在的位置:

假設,Z是最終依賴關係。我們創建單元測試而不模擬它。所有邊界條件都是已知的。 100%的覆蓋率是必須的。之後,我們說Z正常工作。如果Z失敗,我們的單元測試必須指明它。
模擬來自工程。建造飛機時沒有人測試每個螺釘和螺栓。
統計方法用於證明工廠生產細節的工作正常。另一方面,對於系統中非常關鍵的部分,花費時間和模擬依賴項的複雜行爲是合理的。是的,它越複雜,測試的可維護性越差。在這裏,我寧願將它們稱爲規格檢查。
是的,您的API和測試都可能是錯誤的,但代碼審查和其他形式的測試可以在一定程度上確保代碼的正確性。一旦這些測試在一些變化後失敗,您或者需要更改規格和相應的測試,或者找到錯誤並用測試覆蓋該案例。

我強烈建議你看羅伊的視頻:感謝http://youtube.com/watch?v=fAb_OnooCsQ