引用单元测试中mock的使用及mock神器jmockit实践中的java单元测试中各种Mock框架对比,就能明白JMockit有多么强大:

JMockit是基于JavaSE5中的java.lang.instrument包开发,内部使用ASM库来动态修改java的字节码,使得java这种静态语言可以想动态脚本语言一样动态设置被Mock对象私有属性,模拟静态、私有方法行为等等,对于手机开发,嵌入式开发等要求代码尽量简洁的情况下,或者对于被测试代码不想做任何修改的前提下,使用JMockit可以轻松搞定很多测试场景。

通过如下方式在maven中添加JMockit的相关依赖:

  1. <dependency>
  2. <groupId>com.googlecode.jmockit</groupId>
  3. <artifactId>jmockit</artifactId>
  4. <version>1.5</version>
  5. <scope>test</scope>
  6. </dependency>
  7. <dependency>
  8. <groupId>com.googlecode.jmockit</groupId>
  9. <artifactId>jmockit-coverage</artifactId>
  10. <version>0.999.24</version>
  11. <scope>test</scope>
  12. </dependency>

JMockit有两种Mock方式:基于行为的Mock方式和基于状态的Mock方式:

引用单元测试中mock的使用及mock神器jmockit实践中JMockit API和工具如下:

(1).基于行为的Mock方式:

非常类似与EasyMock和PowerMock的工作原理,基本步骤为:

1.录制方法预期行为。

2.真实调用。

3.验证录制的行为被调用。

通过一个简单的例子来介绍JMockit的基本流程:

要Mock测试的方法如下:

  1. public class MyObject {
  2. public String hello(String name){
  3. return "Hello " + name;
  4. }
  5. }

使用JMockit编写的单元测试如下:

  1. @Mocked  //用@Mocked标注的对象,不需要赋值,jmockit自动mock
  2. MyObject obj;
  3. @Test
  4. public void testHello() {
  5. new NonStrictExpectations() {//录制预期模拟行为
  6. {
  7. obj.hello("Zhangsan");
  8. returns("Hello Zhangsan");
  9. //也可以使用:result = "Hello Zhangsan";
  10. }
  11. };
  12. assertEquals("Hello Zhangsan", obj.hello("Zhangsan"));//调用测试方法
  13. new Verifications() {//验证预期Mock行为被调用
  14. {
  15. obj.hello("Hello Zhangsan");
  16. times = 1;
  17. }
  18. };
  19. }

JMockit也可以分类为非局部模拟与局部模拟,区分在于Expectations块是否有参数,有参数的是局部模拟,反之是非局部模拟。

而Expectations块一般由Expectations类和NonStrictExpectations类定义,类似于EasyMock和PowerMock中的Strict Mock和一般性Mock。

用Expectations类定义的,则mock对象在运行时只能按照 Expectations块中定义的顺序依次调用方法,不能多调用也不能少调用,所以可以省略掉Verifications块;

而用NonStrictExpectations类定义的,则没有这些限制,所以如果需要验证,则要添加Verifications块。

上述的例子使用了非局部模拟,下面我们使用局部模拟来改写上面的测试,代码如下:

  1. @Test
  2. public void testHello() {
  3. final MyObject obj = new MyObject();
  4. new NonStrictExpectations(obj) {//录制预期模拟行为
  5. {
  6. obj.hello("Zhangsan");
  7. returns("Hello Zhangsan");
  8. //也可以使用:result = "Hello Zhangsan";
  9. }
  10. };
  11. assertEquals("Hello Zhangsan", obj.hello("Zhangsan"));//调用测试方法
  12. new Verifications() {//验证预期Mock行为被调用
  13. {
  14. obj.hello("Hello Zhangsan");
  15. times = 1;
  16. }
  17. };
  18. }

模拟静态方法:

  1. @Test
  2. public void testMockStaticMethod() {
  3. new NonStrictExpectations(ClassMocked.class) {
  4. {
  5. ClassMocked.getDouble(1);//也可以使用参数匹配:ClassMocked.getDouble(anyDouble);
  6. result = 3;
  7. }
  8. };
  9. assertEquals(3, ClassMocked.getDouble(1));
  10. new Verifications() {
  11. {
  12. ClassMocked.getDouble(1);
  13. times = 1;
  14. }
  15. };
  16. }

模拟私有方法:

如果ClassMocked类中的getTripleString(int)方法指定调用一个私有的multiply3(int)的方法,我们可以使用如下方式来Mock:

  1. @Test
  2. public void testMockPrivateMethod() throws Exception {
  3. final ClassMocked obj = new ClassMocked();
  4. new NonStrictExpectations(obj) {
  5. {
  6. this.invoke(obj, "multiply3", 1);//如果私有方法是静态的,可以使用:this.invoke(null, "multiply3")
  7. result = 4;
  8. }
  9. };
  10. String actual = obj.getTripleString(1);
  11. assertEquals("4", actual);
  12. new Verifications() {
  13. {
  14. this.invoke(obj, "multiply3", 1);
  15. times = 1;
  16. }
  17. };
  18. }

设置Mock对象私有属性的值:

我们知道EasyMock和PowerMock的Mock对象是通过JDK/CGLIB动态代理实现的,本质上是类的继承或者接口的实现,但是在java面向对象编程中,基类对象中的私有属性是无法被子类继承的,所以如果被Mock对象的方法中使用到了其自身的私有属性,并且这些私有属性没有提供对象访问方法,则使用传统的Mock方法是无法进行测试的,JMockit提供了设置Mocked对象私有属性值的方法,代码如下:
被测试代码:
  1. public class ClassMocked {
  2. private String name = "name_init";
  3. public String getName() {
  4. return name;
  5. }
  6. private static String className="Class3Mocked_init";
  7. public static String getClassName(){
  8. return className;
  9. }
  10. }

使用JMockit设置私有属性:

  1. @Test
  2. public void testMockPrivateProperty() throws IOException {
  3. final ClassMocked obj = new ClassMocked();
  4. new NonStrictExpectations(obj) {
  5. {
  6. this.setField(obj, "name", "name has bean change!");
  7. }
  8. };
  9. assertEquals("name has bean change!", obj.getName());
  10. }

使用JMockit设置静态私有属性:

  1. @Test
  2. public void testMockPrivateStaticProperty() throws IOException {
  3. new NonStrictExpectations(Class3Mocked.class) {
  4. {
  5. this.setField(ClassMocked.class, "className", "className has bean change!");
  6. }
  7. };
  8. assertEquals("className has bean change!", ClassMocked.getClassName());
  9. }
(2).基于状态的Mock方式:
JMockit上面的基于行为Mock方式和传统的EasyMock和PowerMock流程基本类似,相当于把被模拟的方法当作黑盒来处理,而JMockit的基于状态的Mock可以直接改写被模拟方法的内部逻辑,更像是真正意义上的白盒测试,下面通过简单例子介绍JMockit基于状态的Mock。
被测试的代码如下:
  1. public class StateMocked {
  2. public static int getDouble(int i){
  3. return i*2;
  4. }
  5. public int getTriple(int i){
  6. return i*3;
  7. }
  8. }
改写普通方法内容:
  1. @Test
  2. public void testMockNormalMethodContent() throws IOException {
  3. StateMocked obj = new StateMocked();
  4. new MockUp<StateMocked>() {//使用MockUp修改被测试方法内部逻辑
  5. @Mock
  6. public int getTriple(int i) {
  7. return i * 30;
  8. }
  9. };
  10. assertEquals(30, obj.getTriple(1));
  11. assertEquals(60, obj.getTriple(2));
  12. Mockit.tearDownMocks();//注意:在JMockit1.5之后已经没有Mockit这个类,使用MockUp代替,mockUp和tearDown方法在MockUp类中
  13. }
修改静态方法的内容:
基于状态的JMockit改写静态/final方法内容和测试普通方法没有什么区别,需要注意的是在MockUp中的方法除了不包含static关键字以外,其他都和被Mock的方法签名相同,并且使用@Mock标注,测试代码如下:
  1. @Test
  2. public void testGetTriple() {
  3. new MockUp<StateMocked>() {
  4. @Mock
  5. public int getDouble(int i){
  6. return i*20;
  7. }
  8. };
  9. assertEquals(20, StateMocked.getDouble(1));
  10. assertEquals(40, StateMocked.getDouble(2));
  11. }
JMockit和PowerMock混用时不兼容问题:
由于PowerMock需要在单元测试类上添加@RunWith(PowerMockRunner.class)注解,用于表面使用PowerMock来执行单元测试,而JMockit不需要指定@RunWith注解,因此当一个单元测试类中混合使用PowerMock和JMockit时,JMockit总是会报错初始化失败,因此我建议不要在同一个单元测试类中混用PowerMock和JMockit。
另外,JMockit要求必须出现在JVM classpath的中Junit前面位置,因此在添加Maven依赖时记得要把JMockit放在Junit最前面,否则同样报错JMockit初始化失败。
 

统计JMockit的单元测试覆盖率:

由于JMockit使用JavaSE5中的java.lang.instrument包开发,因此一般的单元测试覆盖率统计插件和工具对其无法工作,必须要借助自带的JMockit coverage才行,对于使用Eclemma插件和maven+sonar方式的单元测试覆盖率统计,分别有如下方法:
(1).Eclemma插件方式:
如果直接使用Eclemma插件来统计单元测试覆盖率,会发现Eclemma长时间挂起阻塞,强行结束时会报错说找不到-javaagent等等,解决方法如下:
在Eclipse中右键选择Coverage as ->Coverage Configurations,配置Junit的JVM参数如下:
-javaagent:"${settings.localRepository}"/com/googlecode/jmockit/jmockit-coverage/0.999.24/jmockit-coverage-0.999.24.jar
其中${settings.localRepository}是maven资源目录,例如:
-javaagent:D:\userdata\administrator\.m2\repository\com\googlecode\jmockit\jmockitcoverage\0.999.24\jmockit-coverage-0.999.24.jar
(2).Maven+Sonar方式:
如果直接使用mvn sonar:sonar命令时,发现任何单元测试无法执行,长时间卡住,强行结束后再次执行时maven工程target目录下surefire目录无法删除,只有重启机器后才能删除,解决方法如下:
在pom文件中添加如下的插件:
  1. <plugin>
  2. <groupId>org.apache.maven.plugins</groupId>
  3. <artifactId>maven-surefire-plugin</artifactId>
  4. <version>2.12</version>
  5. <configuration>
  6. <argLine>-javaagent:"${settings.localRepository}"/com/googlecode/jmockit/jmockit-coverage/0.999.24/jmockit-coverage-0.999.24.jar</argLine>
  7. </configuration>
  8. </plugin>

Jmockit使用的更多相关文章

  1. JMockit

    [TOC] 简介 JMockit是基于JavaSE5中的java.lang.instrument包开发,内部使用ASM库来动态修改java的字节码,使得java这种静态语言可以想动态脚本语言一样动态设 ...

  2. 使用JUnit4与JMockit进行打桩测试

    1. 何为Mock 项目中各个模块,各个类之间会有互相依赖的关系,在单元测试中,我们只关心被测试的单元,对于其依赖的单元并不关心(会有另外针对该单元的测试). 比如,逻辑层A类依赖了数据访问层B类的取 ...

  3. JMockit使用总结

    Jmockit可以做什么 使用JMockit API来mock被依赖的代码,从而进行隔离测试. 类级别整体mock和部分方法重写 实例级别整体mock和部分mock mock静态方法.私有变量.局部方 ...

  4. jmockit学习

    下图为jmockit 类图.在我们编写代码时几乎都会用到Expectations(期望)和Verifications(校验),二者均继承自Invacations. 常会用到的注解有:@Mocked @ ...

  5. jmockit学习总结

    mock类型和实例 从依赖的测试代码调用的方法和构造函数是mock(模拟)的目标. Mocking提供了我们需要的机制,以便将被测试的代码与(一些)依赖关系隔离开来.我们通过声明适当的模拟字段和/或模 ...

  6. Jmockit之mock特性详解

    本文是Jmockit学习过程中,根据官网所列的工具特性进行解读. 1.调用次数约束(Invocation count constraints) 可以通过调用计数约束来指定预期和/或允许匹配给定期望的调 ...

  7. 单元测试系列:Mock工具Jmockit使用介绍

    更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6760272.html Mock工具Jm ...

  8. JMockit常用操作

    JMockit常用操作 2017-11-30 转自:http://blog.csdn.net/foreverling/article/details/51234149 目录 1 基本概念  1.1 常 ...

  9. 单元测试系列之十一:Jmockit之mock特性详解

    本文是Jmockit学习过程中,根据官网所列的工具特性进行解读. 1.调用次数约束(Invocation count constraints) 可以通过调用计数约束来指定预期和/或允许匹配给定期望的调 ...

随机推荐

  1. mongodb 安装后 出现警告:** WARNING: soft rlimits too low. Number of files is 256, should be at least 1000

    警告问题:当前mongodb 支持的最大文件数有256个,但是推荐至少1024个. 解决办法: 1.关闭现在打开的mongodb 终端窗口 2.重新打开终端并运行一下命令: sudo launchct ...

  2. ionic 运用pouchdb/sqlite 数据库做本地存储

    配置数据库环境需要3步: 1.安装slqite插件 在ionic 工程目录对应终端执行一下命令: npm install cordova-plugin-sqlite 2.安装pouchdb 在ioni ...

  3. Visual Studio 2012 常用快捷键

    1. 强迫智能感知:Ctrl+J:2.强迫智能感知显示参数信息:Ctrl-Shift-空格:3.格式化整个块:Ctrl+K+F4. 检查括号匹配(在左右括号间切换): Ctrl +]5. 选中从光标起 ...

  4. PL/SQL不支持64位Oracle Client 解决办法

    解决X64操作系统PL/SQL连接报错问题 make sure you have the 32 bits oracle client installed 说明PLSQL Developer并不支持Or ...

  5. Java基础之在窗口中绘图——绘制星星(StarApplet 1)

    Applet程序. 可以把更复杂的几何形状定义为GeneralPath类型的对象.GeneralPath可以是直线.Quad2D曲线和Cubic2D曲线的结合体,甚至可以包含其他GeneralPath ...

  6. 使用 robotframework 自动化测试系列 一 -----简介

    robotframework 是自动化测试框架. Robot Framework是一款python编写的功能自动化测试框架.具备良好的可扩展性,支持关键字驱动,可以同时测试多种类型的客户端或者接口,可 ...

  7. Leetcode: Can I Win

    In the "100 game," two players take turns adding, to a running total, any integer from 1.. ...

  8. spring使用elasticsearch 5.x

    elasticsearch客户端选择 这里使用transport建立elasticsearch客户端 applicationContext.xml配置,属性可以采用读取属性文件的方式.参考类Prope ...

  9. 关于Beta分布、二项分布与Dirichlet分布、多项分布的关系

    在机器学习领域中,概率模型是一个常用的利器.用它来对问题进行建模,有几点好处:1)当给定参数分布的假设空间后,可以通过很严格的数学推导,得到模型的似然分布,这样模型可以有很好的概率解释:2)可以利用现 ...

  10. Django开发笔记之数据库的设计

    后台采用Django开发,可以体会到开发的便利之处,对于一个项目来说,首先最重要的是数据库的设计,那么在Django下数据库设计主要是如下步骤: 1,需求分析,这点子不用多说,而我也深刻体会到了没有原 ...