zoukankan      html  css  js  c++  java
  • 单元测试之Stub和Mock

    在做单元测试的时候,我们会发现我们要测试的方法会引用很多外部依赖的对象,比如:(发送邮件,网络通讯,记录Log, 文件系统 之类的)。 而我们没法控制这些外部依赖的对象。  为了解决这个问题,我们需要用到Stub和Mock来模拟这些外部依赖的对象,从而控制它们

    阅读目录

    1. 实例
    2. 设计测试用例
    3. 什么是外部依赖
    4. Stub和Mock的相同处
    5. Stub和Mock的区别
    6. 良好的产品代码才能单元测试
    7. Mock框架
    8. 源代码下载

    实例

    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用来判断测试通过还是失败

    良好的产品代码才能单元测试

    如果产品代码是下面那样,你就没办法测试了。 因为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("to@test.com", "from@test.com", "WebSerive log error", e.Message);
    }
    }
    }
    }

    Mock框架

    其实我们没有必要自己写MockEmailService方法。  已经有现成的Mock框架可以用了, .NET中有Rhino Mock 和 Moq,  这两个框架比较好用

    源代码下载

    点击此处下载完整的源代码, 请用VS2008打开

    [参考] The Art of Unit Testing

  • 相关阅读:
    android 扇形菜单
    Gradle 教程:第一部分,安装【翻译】
    Android Studio 创建虚拟机失败 Failed to load 解决办法
    【翻译】Best Practices for User interface android 适配不同屏幕、不同分辨率
    [转]QT 4.8 静态库编译方法
    使用windows资源管理器打开jar
    一步步理解typedef
    模拟Chrome皮肤
    FAT12格式的引导区实现
    Android-获取Html元素
  • 原文地址:https://www.cnblogs.com/TankXiao/p/2366073.html
Copyright © 2011-2022 走看看