2010-06-28 94 views
12

我已經開始使用moq進行嘲弄。有人能向我解釋嚴格和非嚴格模擬的概念嗎?他們如何可以在moq中使用?什麼是嚴格和非嚴格的模擬?

編輯: 在哪種情況下我們使用哪種類型的模擬?

+0

想知道嚴格vs非嚴格與[angular $ httpBackend文檔](https://docs.angularjs)中描述的「嚴格單元測試」和「寬鬆(黑盒)單元測試」之間的區別是否相同。 org/api/ngMock/service/$ httpBackend) – 2016-12-15 20:18:52

回答

11

我不確定moq是否具體,但是這裏有多嚴格的mock在Rhino中工作。我聲明,我希望我的對象foofoo.Bar呼叫:

foo.Expect(f => f.Bar()).Returns(5); 

如果調用代碼不

foo.Bar(); 

然後我很好,因爲預期是否完全滿足。

但是,如果調用代碼是:

foo.Quux(12); 
foo.Bar(); 

然後我的期望失敗了,因爲我沒有明確預期foo.Quux通話。

總之,如果有什麼不同於預期,嚴格的模擬將立即失敗。另一方面,非嚴格的模擬(或存根)將很樂意「忽略」foo.Quux的呼叫,並且它應該返回default(T),返回類型爲的foo.Quux

犀牛的創造者recommends that you avoid strict mocks(而且更喜歡存根),因爲您通常不希望您的測試在接收到上述意外呼叫時失敗。當您必須修復依賴於原始行爲的幾十個測試時,它會使得重構代碼變得更加困難。

+2

你在哪裏指定它是一個嚴格的模擬? – Sandbox 2010-06-28 16:24:54

+1

@Sandbox:你可以在[Mock'構造函數中使用'MockBehavior'參數](http://www.clariusconsulting.net/labs/moq/html/DD4BEE30.htm)指定strict或non-strict。默認行爲(當不指定'MockBehavior'時)似乎是非嚴格的(他們稱之爲「鬆散」)。 – 2010-06-28 16:32:47

+0

我認爲將Mark的評論納入答案本身是很好的;包括如何創建嚴格與非嚴格的解釋以及提及別名「寬鬆」。 – 2016-12-15 20:21:25

3

曾經遇到過Given/When/Then?

  • 考慮上下文
  • 當我執行一些事件
  • 然後的結果應該發生

這種模式出現在BDD的場景,也有相關的單元測試。

如果您正在設置上下文,您將使用該上下文提供的信息。例如,如果您正在通過Id查找某個內容,那就是上下文。如果它不存在,測試將不會運行。在這種情況下,你想使用NiceMock或者Stub或者其他的 - Moq默認的運行方式。

如果你想驗證結果,你可以使用Moq的驗證。在這種情況下,你想記錄相關的交互。幸運的是,這也是Moq的默認運行方式。如果發生了某種情況,您對該測試不感興趣,它不會投訴。

StrictMock適用於當您不希望發生意外的交互時。這是多麼古老的嘲笑框架用於運行。如果你正在做BDD風格的例子,你可能不會想要這個。與分離你感興趣的行爲方面相比,它有一種傾向,使得測試有點脆弱和難以閱讀。你必須爲情境和結果設定預期,對於所有將發生的結果,無論他們是否感興趣。例如,如果您正在測試一個控制器並嘲笑您的驗證器和存儲庫,並且想驗證您是否已經保存了您的對象,那麼您需要嚴格模擬,您還必須驗證您是否已經首先驗證對象。我更喜歡在單獨的例子中看到這兩方面的行爲,因爲它讓我更容易理解控制器的價值和行爲。

在過去四年裏,我還沒有找到一個需要使用嚴格模擬的例子 - 要麼是我想驗證的結果(即使我驗證被調用的次數)或上下文爲此我可以判斷我是否對所提供的信息做出了正確的迴應。因此,在回答你的問題:

  • 非嚴格模擬:通常
  • 嚴格的Mock:最好永遠

NB:我強烈地偏向BDD,所以硬核TDD可能不同意我的看法,並且這對他們的工作方式是正確的。

+0

如何避免方法更改功能的問題,然後您沒有任何測試驗證此新功能,因爲您的所有單元測試仍然通過? – 2018-01-27 19:55:19

+0

@DanielLorenz如果您要更改功能,請先寫一個失敗的測試;無論您使用的是什麼樣的模擬,這都是TDD週期的核心部分。如果您指的是無意中更改的方法,那麼仔細考慮具有可讀性的測試並減少發生這種情況的可能性。 – Lunivore 2018-01-29 17:23:57

+0

我認爲最好的辦法是讓所有的測試都鬆散,除非是嚴格的。那樣的話,如果你做了一堆這樣的改變,那麼1次測試就會失敗,並會提醒你確保你已經覆蓋了所有的新案例,但是修復那個測試並不是那麼痛苦。 – 2018-01-29 17:54:37

0

這是一個很好的article
我通常最終會爲這樣的事情

public class TestThis { 

    private final Collaborator1 collaborator1; 
    private final Collaborator2 collaborator2; 
    private final Collaborator2 collaborator3; 

    TestThis(Collaborator1 collaborator1, Collaborator2 collaborator2, Collaborator3 collaborator3) { 
     this.collaborator1 = collaborator1; 
     this.collaborator2 = collaborator2; 
     this.collaborator3 = collaborator3; 
    } 

    public Login login(String username) { 
     User user = collaborator1.getUser(username); 
     collaborator2.notify(user); 
     return collaborator3.login(user); 
    } 

} 

...我用嚴格的嘲弄爲3名合作者測試登錄(用戶名)。我看不到嚴格的模擬不應該被使用。

0

我有一個簡單的約定:

  1. 時使用測試(SUT)系統將調用委託到下面的嘲笑層沒有真正修改或申請任何業務邏輯傳遞給參數嚴格嘲笑本身。

  2. 當SUT將業務邏輯應用於傳遞給自身的參數並將某些派生/修改值傳遞給模擬圖層時,使用鬆散模擬。

對於如: 比方說我們有它有兩個方法的數據庫提供商StudentDAL:

的數據訪問接口看起來像下面:

public Student GetStudentById(int id); 
public IList<Student> GetStudents(int ageFilter, int classId); 

消耗這個DAL的實現如下所示:

public Student FindStudent(int id) 
{ 
    //StudentDAL dependency injected 
    return StudentDAL.GetStudentById(id); 
    //Use strict mock to test this 
} 
public IList<Student> GetStudentsForClass(StudentListRequest studentListRequest) 
{ 
    //StudentDAL dependency injected 
    //age filter is derived from the request and then passed on to the underlying layer 
    int ageFilter = DateTime.Now.Year - studentListRequest.DateOfBirthFilter.Year; 
    return StudentDAL.GetStudents(ageFilter , studentListRequest.ClassId) 
    //Use loose mock and use verify api of MOQ to make sure that the age filter is correctly passed on. 

}