在做單元測試的時候,我們會發現我們要測試的方法會引用很多外部依賴的對象,比如:(發送郵件,網絡通訊,記錄Log, 文件系統 之類的)。 而我們沒法控制這些外部依賴的對象。 為了解決這個問題,我們需要用到Stub和Mock來模擬這些外部依賴的對象,從而控制它們
實例
Analyze類會檢查filename的長度,如果小于8,我們就會使用一個實現IWebService的類來記錄錯誤.
我們需要給Analyze方法寫單元測試。
public class LogAnalyzer { private IWebService service; private IEmailService email; public IWebService Service { get { return service; } set { service = value; } } public IEmailService Email { get { return email; } set { email = value; } } public void Analyze(string fileName) { if (fileName.Length < 8) { try { service.LogError("the file name is to short" + fileName); } catch (Exception e) { email.SendEmail("From@test.com", "To@test.com", "IWebServiceFailed", e.Message); } } } } |
測試用例一:
fileName= "c:\test\test.txt" (長度大于8),
期待測試結果: 不會發郵件
測試用例二:
fileName="c:\",(長度小于8), 并且記log失敗 。
期待測試結果: 發郵件
如果給Analyze方法寫單元測試,為了實現測試用例二。 這時候我們就會碰到兩個問題。
第一: 我們無法控制讓Service對象記log時拋出異常. 因為Serveice對象我們無法控制
第二: 我們無法判斷,Email對象是否發送了Email, (我們不能去Outlook查看收到郵件沒有,這樣就不是自動化了)
外部依賴對象
對于LogAnalyzer對象來說, Service和Email就是兩個外部依賴對象. 我們需要自己寫Stub和Mock來模擬這兩個外部依賴對象。這樣我們才能控制他們。
我們在測試的代碼中新建StubWebService和MockEmailService.這兩個class分別實現了IWebService和IEmailService.
public class StubWebService : IWebService { public void LogError(string message) { throw new Exception("StubWebService throw exception"); } } public class MockEmailService : IEmailService { public string To; public string From; public string Subject; public string Message; public void SendEmail(string to, string from, string subject, string message) { To = to; From = from; Subject = subject; Message = message; } } |
工作流程圖如下
最后我們來看看我們的測試代碼,
我們把StubWebService和MockEmailService兩個類的實例注入到產品代碼中。(因為多態特性嘛)。
通過控制StubWebService中的LogError方法,拋出一個異常。
然后判斷MockEmailService中的SendEmail方法有沒有被調用. 被調用了說明發送了Email(我們不需要真的收到一封郵件,因為SendEmail功能是IEmailService實現的,)
[TestMethod] public void TestMethod1() { StubWebService stubWebService = new StubWebService(); MockEmailService mockEmailSender = new MockEmailService(); LogAnalyzer log = new LogAnalyzer(); log.Emailservice = mockEmailSender; log.WebService = stubWebService; // Act string tooShortFileName = "1.txt"; log.Analyze(tooShortFileName); // Assert Assert.AreEqual("to@test.com", mockEmailSender.To); Assert.AreEqual("from@test.com", mockEmailSender.From); Assert.AreEqual("WebSerive log error", mockEmailSender.Subject); } |
Stub和Mock的相同處
從上面的例子我們可以看出, Stub和Mock都是模擬外部依賴,以便我們能控制。
Stub 和Mock 的區別
Stub是完全模擬一個外部依賴, 而Mock用來判斷測試通過還是失敗
原文轉自:http://www.uml.org.cn/Test/201210171.asp