单元测试中使用Mock对象

  • 单元测试中使用Mock对象
    • 一、简单的替换
    • 二、Mock 对象
    • 三、测试 Servlet

单元测试中使用Mock对象

一、简单的替换

假设在代码中,你调用你自己的 getTime () 来返回系统当前的日期和时间 :

public long getTime() {return System.currentTimeMillis();}

通常建议对应用程序范围外的功能调用进行包装,从而能够更好地封装它们;如上述代码所示,我们是把当前时间的概念包装在自己写的代码里面, 所以调试就容易了一些:

public long getTime() {if (debug) {return debug_cur_time;}else {return System.currentTimeMillis();}}

还可以使用其他的调试路径来操纵 “当前时间” 这个系统概念,从而让那些不这样做而需要等很长时间才能发生的事件马上发生。
只有在代码一致调用你自己的getTime ()、完全没有直接调用Java方法System. currentTimeMillis ()的时候这种方法才有效;但是我们需要的是一种更加干净、更加面向对象化且同时可以实现相同功能的方法。

二、Mock 对象

Mock 对象就是真实对象在调试期的替代品;在许多情况下,mock对象都可以给我们带来帮助:

  • 真实对象具有不可确定的行为(产生不可预测的结果, 如股票行情);
  • 真实对象很难被创建;
  • 真实对象的某些行为很难触发(如网络错误);
  • 真实对象令程序的运行速度很慢;
  • 真实对象有(或者是)用户界面;
  • 测试需要询问真实对象是如何被调用的(例如测试可能需要验证某个回调函数是否被调用);
  • 真实对象实际上并不存在(当需要和其他开发小组或者新的硬件系统打交道的时候, 这是一个普遍问题)

借助 Mock 对象,我们就可以解决上面提到的所有问题;在使用 Mock 对象进行测试的时候, 总共有3个关键步骤, 分别是:

  1. 使用一个接口来描述这个对象;
  2. 为产品代码实现这个接口;
  3. 以测试为目的, 在 Mock 对象中实现这个接口.

因为被测试代码只会通过接口来引用对象,所以它完全可以不知道它引用的究竟是真实对象还是 mock 对象。
创建针对真实环境因素的几个对象,其中一个因素就是当前时间:

    public interface Envirorunental {public long getTime();//  ...}

编写真实的实现代码:

public class SystemEnvironment implements Environmental{public long getTime() {return System.currentTimeMillis();}//other method
}

Mock的实现:

public class MockSystemEnvirorunent implements Environmental{public long getTime() {return current_time;}public void setTime(long aTime) {current_time = aTime;}private long current_time;//...
}

在mock实现里面添加了一个额外的方法 setTime() (以及对应的私有变量), 这让可以控制 Mock 对象;
现在假设编写一个依赖于 getTime() 方法的新方法:Checker.java

public class Checker {public Checker(Environmental anEnv) {env = anEnv;}public void reminder() {Calendar cal = Calendar.getInstance();cal.setTimeInMillis(env. getTime());int hour = cal.get(Calendar.HOUR_OF_DAY);if (hour >= 17) {   //5:00PMenv.playWavFile("quit_whistle.wav");}}// ...private Environmental env;
}

在产品环境中, 当初始化这个类的对象时,传入的是一个真实的 SystemEnvironment; 而另一方面, 测试代码传入的则是MockSystemEnvironment;
使用 env. getTime() 的被测试代码并不知道测试环境和真实环境之间的区别, 因为它们都实现了相同的接口;
现在可以借助 Mock对象, 通过把时间设置为已知值, 并检查行为是否如预期那样来编写测试了.
除了已经展示的getTime () , Environmental 接口还有一个 playWavFile() 函数(Checker用到了);通过给 mock 对象添加一些额外的支持代码, 从而能够在不倾听计算机喇叭的情况下, 添加测试来观察它是否被调用了。

   public class MockSystemEnvironment implements Environmental{public long getTime() {return current_time;}public void setTime(long aTime) {current_time = aTime;}private long current_time;//...public void playWavFile(String filename){playedWav = true;}public boolean wavwasPlayed(){return playedWav;}public void resetwav() {playedWav = false;}private boolean playedWav = false;
}

整合上方代码:TestChecker.java

public class TestChecker extends TestCase {public void testQuittingTime() {MockSystemEnvironment env = new MockSystemEnvironment();Calendar cal = Calendar.getInstance();cal.set(Calendar.YEAR, 2004);cal.set(Calendar.MONTH, 10);cal.set(Calendar. DAY_OF_MONTH, 1) ;cal.set(Calendar.HOUR_OF_DAY, 16);cal.set(Calendar.MINUTE, 55);long tl = cal.getTimeInMillis();env.setTime(tl);Checker checker = new Checker(env);// Run the checkerchecker.reminder();//Nothing should have been played yetassertFalse(env.wavwasPlayed());// Advance the time by 5 minutestl += (5 * 60 * 1000);env.setTime (tl);//Now run the checkerchecker.reminder();//Should have played nowassertTrue(env.wavwasPlayed());//Reset the flag so we can try againenv.resetwav();//Advance the time by 2 hours and checktl += 2 * 60 * 60 * 1000;env.setTime(tl);checker.reminder();assertTrue(env.wavwasPlayed());}
}
  • 代码创建了一个应用环境的 mock 版本;并设置了我们将使用的假的时间, 然后将它们设置给了mock环境对象;
  • 调用 reminder(), 这将(不知情地)使用mock环境。调用断言检查wav文件是不是还没播放, 因为在mock对象的环境中,
    此时还不是quitting time;
  • 但是我们将很快调整时间;把mock时间调整到刚好是 quitting time;然后再一次调用reminder()函数。这次,声音已经被播放过了,因而调用断言确认了.wav文件这次已经播放过了;
  • 最后重设 Mock 环境的 .wav 文件的标志, 并且测试再过两个小时之后的情况。

因为已经有了一个提供所有系统功能的现成接口,所以会使用接口而不是直接调用诸如 System.currentTimeMillis() 这样的方法, Mock 对象通过接口拥有了控制一切行为的能力。

三、测试 Servlet

下面的代码列表展示了一个把华氏温标转换为摄氏温标的servlet的部分源代码:

 public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {String str_f = req.getParameter("Fahrenheit");res.setContentType("text/html");PrintWriter out = res.getWriter();try {int temp_f = Integer.parseInt(str_f);double temp_c = (temp_f - 32) * 5.0 / 9.0;out.println("Fahrenheit: " + temp_f + ", Celsius: " + temp_c);}catch (NumberFormatException e){out.println( "Invalid temperature: " + str_f);}

当 servlet 容器接受到请求时, 它自动调用 servlet 的方法doGet(), 并传递给两个参数: request 和 response;
request 参数包含关于请求的信息,servlet使用这个参数来获得华氏温标的值;然后这个温度值被转换为摄氏温标;最后这个结果被发送回给用户。
如果在转换过程中发生了错误(可能用户给温度项中输入的是"boo!" 而不是有效的温度值),捕获这个异常并把错误报告于response 中。
这个代码片断运行于一个相当复杂的环境之中:它需要一个Web服务器和一个servlet容器,并且它需要一个浏览器来与它交互。这几乎没法做自动化的单元测试。Mock对象可以解决这个问题。
servlet代码的接口是相当简单的:它接受两个参数, 一个request和一个response:request对象必须能够在它的getParameter()方法被调用时提供合理的字符串, 而response对象必须能够支持 setContentType () 和 getWriter ()。
具体做法这与在前面的例子中设置假的时间的做法类似:

public class TestTempServlet extends TestCase {public void test_bad_parameter() throws Exception {TemperatureServlet s = new TemperatureServlet();MockHttpServletRequest request = new MockHttpServletRequest();MockHttpServletResponse response = new MockHttpServletResponse();request.setupAddParameter("Fahrenheit", "boo!");response.setExpectedContentType("text /html ");s.doGet(request, response);response.verify();assertEquals("Invalid temperature: boo!\ n", response.getOutputStreamContents());}public void test_boil() throws Exception {TemperatureServlet s = new TemperatureServlet();MockHttpServletRequest request = new MockHttpServletRequest();MockHttpServletResponse response = new MockHttpServletResponse();request.setupAddParameter("Fahrenheit","212");response.setExpectedContentType("text/html");s.doGet(request.response);response.verify();assertEquals("Fahrenheit: 212, Celsius: 100.0\ n",response.getOutputStreamContents());}
}

使用 MockHttpServletRequest 对象来设置要运行测试的上下文;在请求对象中设置参数 Fahrenheit 为值"boo!",这等价于用户在浏览器的相应表单项中填入"boo!"; Mock对象消除了在测试运行时手工输入的必要;
告诉 response对象 期望被测方法设置 response 的 content type为 text/html ;被测方法执行之后,告诉 response 对象验证这是否发生了;Mock 对象消除了人工检查结果的需要;
Mock 对象还能记录给它们传递的数据。在本例中,response对象接收了servlet 需要显示在浏览器上的文本,可以查询这些值来检查返回的文本是否如预期一样。
内容来源-----《单元测试之道–使用Juint》

单元测试中使用Mock对象相关推荐

  1. Mock和Java单元测试中的Mock框架Mockito介绍

    什么是Mock? 在面向对象程序设计中,模拟对象(英语:mock object,也译作模仿对象)是以可控的方式模拟真实对象行为的假的对象.程序员通常创造模拟对象(mock object)来测试其他对象 ...

  2. Error —— 单元测试中如何Mock application文件中的数据?

    问题 单元测试的代码中,有从application.properties中读取的变量. 但在单元测试中,无法读取配置文件,变量默认为空,导致单元测试不通过.如何在单元测试中mock配置文件中的变量呢? ...

  3. 单元测试中使用mock最好不要使用easymock而应该使用powermock

    视频参考汪文君powermock视频教程相当的经典 转载于:https://www.cnblogs.com/kebibuluan/p/9223054.html

  4. mockito mock void方法_纯干货,浅谈Mockito在单元测试中的实际应用

    本文接上文"接口方没写代码,对接方只能停工吗?",在这里简单介绍Mockito在单元测试中的实际应用.本文使用场景较单一,如有雷同,不甚荣幸,闲言少叙,开门见山.本文将使用mock ...

  5. 单元测试中的打桩技术

    一.桩是什么 桩,或称桩代码,是指用来代替关联代码或者未实现代码的代码.如果用函数B1来代替B,那么,B称为原函数,B1称为桩函数.打桩就是编写或生成桩代码. 二.打桩的用途 打桩的目的主要有:隔离. ...

  6. ASP.NET Core 单元测试:如何Mock Url.Page()

    点击上方蓝字关注"汪宇杰博客" 导语 在 ASP.NET Core 中,当你在 UrlHelperExtensions 类上使用扩展方法时,很难在单元测试中编写Mock.因为Moq ...

  7. 单元测试中使用Mockito模拟对象

    单元测试应该小巧玲珑,轻盈快捷.然而,一个待测的对象可能依赖另一个对象.它可能需要跟数据库.邮箱服务器.Web Service.消息队列等服务进行交互.但是,这些服务可能在测试过程中不可用.假设这些服 ...

  8. 【全面解析Mock】Mock在单元测试中扮演一个什么角色?

    目录 一.Mock在单元测试中扮演一个什么角色 二.测试准备 三.使用Mock的理由 四.使用Python Mock 五.MagicMock类 六.mock.create_autospce 七.moc ...

  9. 使用 @MockBean 和 @SpyBean 解决 SpringBoot 单元测试中 Mock 类装配的问题

    最近在做某个项目的时候一直使用 @MockBean 来解决单元测试中 Mock 类装配到被测试类的问题.这篇文章主要介绍了 @MockBean 的使用例子以及不使用 @MockBean 而使用@Spy ...

最新文章

  1. VS Code 调试 OneFlow
  2. 判断两个对象是否相等java_判断两个对象的内容是否相等
  3. mysql ssl连接是什么_mysql 的ssl连接是什么
  4. Rails测试《十一》添加邮件发送程序及测试邮件发送程序
  5. 小试---EF5.0入门实例1
  6. gis计算各省河流长度_用河流和各方解释安全漏洞
  7. python 识别 None,NaN,null,‘‘,‘ ‘ 等无意义的值
  8. RuntimeError: Python is not installed as a framework 错误解决方案
  9. oracle笔记(2010-1-30)
  10. php常用数组,php常用数组函数
  11. 湖工微型计算机及原理题目,2017年湖北工业大学电气与电子工程学院942微机原理与应用考研导师圈点必考题汇编...
  12. x61 linux 驱动 无线网卡,联想ThinkPad X61无线网卡驱动
  13. 以太网交换机和路由器的区别(转载)
  14. html5 图片上传进度条,html5异步上传图片显示上传文件进度条
  15. 计算机网络:网络地址-MAC地址、IP地址、子网掩码
  16. 4.各种动物英语表示
  17. 想把静态网页放到服务器上,怎么样让所有人都去访问到
  18. 攒机笔记十二:路由器
  19. 【爬树合集】难啃的骨头——红黑树
  20. iOS开发实战之搜索控制器UISearchController使用

热门文章

  1. html5怎么调用cur指针,鼠标指针.cur.ani导出与制作
  2. ThinkPad 宝典全集
  3. React 360 初体验介绍与环境搭建
  4. python 列表 元祖_Python_列表与元祖
  5. 微信开发 -- 二维码生成
  6. 计算机网络技术2020,计算机网络技术超星2020试题及答案
  7. xctf攻防世界 MISC高手进阶区 misc1
  8. COGS 2057. [ZLXOI2015]殉国
  9. Python个人项目1 --------电商项目
  10. 真相,为什么大厂一边裁员,一边招人?