單元測試的mock和stub

   微笑還是一個轉載:http://www.cnblogs.com/TankXiao/archive/2012/03/06/2366073.html

在做單元測試的時候,我們會發現我們要測試的方法會引用很多外部依賴的對象,比如:(發送郵件網絡通訊記錄Log文件系統 之類的)。 而我們沒法控制這些外部依賴的對象。  爲了解決這個問題,我們需要用到Stub和Mock來模擬這些外部依賴的對象,從而控制它們


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("[email protected]", "[email protected]", "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來模擬這兩個外部依賴對象。這樣我們才能控制他們。

我們在測試的代碼中新建StubWebServiceMockEmailService.這兩個class分別實現了IWebServiceIEmailService

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("[email protected]", mockEmailSender.To);
    Assert.AreEqual("[email protected]", mockEmailSender.From);
    Assert.AreEqual("WebSerive log error", mockEmailSender.Subject);
}

Stub 和Mock 的區別

Stub是完全模擬一個外部依賴而Mock用來判斷測試通過還是失敗



如果產品代碼是下面那樣,你就沒辦法測試了。 因爲WebService和EmailService兩個類沒有繼承接口。我們無法把StubWebService和MockEmailService兩個類注入到產品代碼。

public class LogAnalyzer
    {
        private WebService webService;
        private EmailService emailService;

        public WebService WebService
        {
            get { return webService; }
            set { webService = value; }
        }

        public EmailService Emailservice
        {
            get { return emailService; }
            set { emailService = value; }
        }

        public void Analyze(string fileName)
        {
            if (fileName.Length < 8)
            {
                try
                {
                    WebService.LogError("Filename too short:" + fileName);
                }
                catch (Exception e)
                {
                    Emailservice.SendEmail("[email protected]", "[email protected]", "WebSerive log error", e.Message);
                }
            }
        }
    }



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章