51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 2210|回复: 1
打印 上一主题 下一主题

[讨论] 单元测试之Stub和Mock

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2018-2-27 16:08:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在做单元测试的时候,我们会发现我们要测试的方法会引用很多外部依赖的对象,比如:(发送邮件,网络通讯,记录Log, 文件系统 之类的)。 而我们没法控制这些外部依赖的对象。  为了解决这个问题,我们需要用到Stub和Mock来模拟这些外部依赖的对象,从而控制它们



阅读目录

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


实例
Analyze类会检查filename的长度,如果小于8,我们就会使用一个实现IWebService的类来记录错误.

我们需要给Analyze方法写单元测试。
  1. public class LogAnalyzer
  2. {
  3.     private IWebService service;
  4.     private IEmailService email;

  5.     public IWebService Service
  6.     {
  7.         get { return service; }
  8.         set { service = value; }
  9.     }

  10.     public IEmailService Email
  11.     {
  12.         get { return email; }
  13.         set { email = value; }
  14.     }

  15.     public void Analyze(string fileName)
  16.     {
  17.         if (fileName.Length < 8)
  18.         {
  19.             try
  20.             {
  21.                 service.LogError("the file name is to short" + fileName);
  22.             }
  23.             catch (Exception e)
  24.             {
  25.                 email.SendEmail("From@test.com", "To@test.com", "IWebServiceFailed", e.Message);
  26.             }
  27.         }
  28.     }
  29. }
复制代码
设计测试用例
测试用例一:

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.
  1. public class StubWebService : IWebService
  2. {
  3.     public void LogError(string message)
  4.     {
  5.         throw new Exception("StubWebService throw exception");
  6.     }
  7. }

  8. public class MockEmailService : IEmailService
  9. {
  10.     public string To;
  11.     public string From;
  12.     public string Subject;
  13.     public string Message;

  14.     public void SendEmail(string to, string from, string subject, string message)
  15.     {
  16.         To = to;
  17.         From = from;
  18.         Subject = subject;
  19.         Message = message;
  20.     }
  21. }
复制代码
工作流程图如下

最后我们来看看我们的测试代码,
我们把StubWebService和MockEmailService两个类的实例注入到产品代码中。(因为多态特性嘛)。
通过控制StubWebService中的LogError方法,抛出一个异常。
然后判断MockEmailService中的SendEmail方法有没有被调用. 被调用了说明发送了Email(我们不需
要真的收到一封邮件,因为SendEmail功能是IEmailService实现的,)
  1. [TestMethod]
  2. public void TestMethod1()
  3. {
  4.     StubWebService stubWebService = new StubWebService();
  5.     MockEmailService mockEmailSender = new MockEmailService();

  6.     LogAnalyzer log = new LogAnalyzer();
  7.     log.Emailservice = mockEmailSender;
  8.     log.WebService = stubWebService;

  9.     // Act
  10.     string tooShortFileName = "1.txt";
  11.     log.Analyze(tooShortFileName);

  12.     // Assert
  13.     Assert.AreEqual("to@test.com", mockEmailSender.To);
  14.     Assert.AreEqual("from@test.com", mockEmailSender.From);
  15.     Assert.AreEqual("WebSerive log error", mockEmailSender.Subject);
  16. }
复制代码
Stub和Mock的相同处
从上面的例子我们可以看出, Stub和Mock都是模拟外部依赖,以便我们能控制。

Stub 和Mock 的区别
Stub是完全模拟一个外部依赖, 而Mock用来判断测试通过还是失败

良好的产品代码才能单元测试
如果产品代码是下面那样,你就没办法测试了。 因为WebService和EmailService两个类没有继承接口。
我们无法把StubWebService和MockEmailService两个类注入到产品代码。
  1. public class LogAnalyzer
  2.     {
  3.         private WebService webService;
  4.         private EmailService emailService;

  5.         public WebService WebService
  6.         {
  7.             get { return webService; }
  8.             set { webService = value; }
  9.         }

  10.         public EmailService Emailservice
  11.         {
  12.             get { return emailService; }
  13.             set { emailService = value; }
  14.         }

  15.         public void Analyze(string fileName)
  16.         {
  17.             if (fileName.Length < 8)
  18.             {
  19.                 try
  20.                 {
  21.                     WebService.LogError("Filename too short:" + fileName);
  22.                 }
  23.                 catch (Exception e)
  24.                 {
  25.                     Emailservice.SendEmail("to@test.com", "from@test.com", "WebSerive log error", e.Message);
  26.                 }
  27.             }
  28.         }
  29.     }
复制代码
Mock框架
其实我们没有必要自己写MockEmailService方法。  已经有现成的Mock框架可以用了, .NET中有Rhino
Mock 和 Moq,  这两个框架比较好用

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-4-20 19:38 , Processed in 0.060029 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表