之前《有效使用Mock编写java单元测试》一文中层介绍过使用EasyMock和PowerMock来编写java单元测试,今天介绍一个更加强大的工具——JMockit。

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

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

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

<dependency><groupId>com.googlecode.jmockit</groupId><artifactId>jmockit</artifactId><version>1.5</version><scope>test</scope></dependency><dependency><groupId>com.googlecode.jmockit</groupId><artifactId>jmockit-coverage</artifactId><version>0.999.24</version><scope>test</scope></dependency>

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

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

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

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

1.录制方法预期行为。

2.真实调用。

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

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

要Mock测试的方法如下:

public class MyObject {public String hello(String name){return "Hello " + name;}
}

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

@Mocked  //用@Mocked标注的对象,不需要赋值,jmockit自动mock
MyObject obj;@Test
public void testHello() {new NonStrictExpectations() {//录制预期模拟行为{obj.hello("Zhangsan");returns("Hello Zhangsan");//也可以使用:result = "Hello Zhangsan";}};assertEquals("Hello Zhangsan", obj.hello("Zhangsan"));//调用测试方法new Verifications() {//验证预期Mock行为被调用{obj.hello("Hello Zhangsan");times = 1;}};
}

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

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

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

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

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

@Test
public void testHello() {final MyObject obj = new MyObject();new NonStrictExpectations(obj) {//录制预期模拟行为{obj.hello("Zhangsan");returns("Hello Zhangsan");//也可以使用:result = "Hello Zhangsan";}};assertEquals("Hello Zhangsan", obj.hello("Zhangsan"));//调用测试方法new Verifications() {//验证预期Mock行为被调用{obj.hello("Hello Zhangsan");times = 1;}};
}

模拟静态方法:

@Test
public void testMockStaticMethod() {new NonStrictExpectations(ClassMocked.class) {{ClassMocked.getDouble(1);//也可以使用参数匹配:ClassMocked.getDouble(anyDouble);result = 3;}};assertEquals(3, ClassMocked.getDouble(1));new Verifications() {{ClassMocked.getDouble(1);times = 1;}};
}

模拟私有方法:

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

@Test
public void testMockPrivateMethod() throws Exception {final ClassMocked obj = new ClassMocked();new NonStrictExpectations(obj) {{this.invoke(obj, "multiply3", 1);//如果私有方法是静态的,可以使用:this.invoke(null, "multiply3")result = 4;}};String actual = obj.getTripleString(1);assertEquals("4", actual);new Verifications() {{this.invoke(obj, "multiply3", 1);times = 1;}};
}

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

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

使用JMockit设置私有属性:

@Test
public void testMockPrivateProperty() throws IOException {final ClassMocked obj = new ClassMocked();new NonStrictExpectations(obj) {{this.setField(obj, "name", "name has bean change!");}};assertEquals("name has bean change!", obj.getName());
}

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

@Test
public void testMockPrivateStaticProperty() throws IOException {new NonStrictExpectations(Class3Mocked.class) {{this.setField(ClassMocked.class, "className", "className has bean change!");}};assertEquals("className has bean change!", ClassMocked.getClassName());
}
(2).基于状态的Mock方式:
JMockit上面的基于行为Mock方式和传统的EasyMock和PowerMock流程基本类似,相当于把被模拟的方法当作黑盒来处理,而JMockit的基于状态的Mock可以直接改写被模拟方法的内部逻辑,更像是真正意义上的白盒测试,下面通过简单例子介绍JMockit基于状态的Mock。
被测试的代码如下:
public class StateMocked {public static int getDouble(int i){return i*2;}public int getTriple(int i){return i*3;}
}
改写普通方法内容:
@Test
public void testMockNormalMethodContent() throws IOException {StateMocked obj = new StateMocked();new MockUp<StateMocked>() {//使用MockUp修改被测试方法内部逻辑@Mockpublic int getTriple(int i) {return i * 30;}};assertEquals(30, obj.getTriple(1));assertEquals(60, obj.getTriple(2));Mockit.tearDownMocks();//注意:在JMockit1.5之后已经没有Mockit这个类,使用MockUp代替,mockUp和tearDown方法在MockUp类中
}
修改静态方法的内容:
基于状态的JMockit改写静态/final方法内容和测试普通方法没有什么区别,需要注意的是在MockUp中的方法除了不包含static关键字以外,其他都和被Mock的方法签名相同,并且使用@Mock标注,测试代码如下:
@Testpublic void testGetTriple() {new MockUp<StateMocked>() {@Mock  public int getDouble(int i){  return i*20;  }};  assertEquals(20, StateMocked.getDouble(1));  assertEquals(40, StateMocked.getDouble(2)); }
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文件中添加如下的插件:
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.12</version><configuration><argLine>-javaagent:"${settings.localRepository}"/com/googlecode/jmockit/jmockit-coverage/0.999.24/jmockit-coverage-0.999.24.jar</argLine></configuration></plugin>

再次执行mvn sonar:sonar命令就可以正常统计出JMockit的单元测试覆盖率。

更多内容请参考JMockit官方文档:http://jmockit.googlecode.com/svn/trunk/www/tutorial.html。

使用JMockit编写java单元测试相关推荐

  1. 教你如何更好的编写JAVA单元测试

    如何更好的编写JAVA单元测试 如各位希望转载或引用,请注明出处,尊重原创,谢谢.如有疑问或错误,欢迎邮件沟通.gitHub地址:https://github.com/thinkingfioa 邮箱地 ...

  2. 如何编写Java单元测试(TC)?

    被测类 public class Student {@Autowiredprivate Card card;public boolean judgeNumber(Long uid) throws Se ...

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

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

  4. Java单元测试(Junit+Mock+代码覆盖率)

    单元测试是编写测试代码,用来检测特定的.明确的.细颗粒的功能.单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的. 单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复.改进 ...

  5. 用JUnit框架实现Java单元测试

    http://tech.it168.com/j/2007-08-27/200708271737659.shtml 单元测试是整个测试流程中最基础的部分,它们要求程序员尽可能早地发现问题,并给予控制,这 ...

  6. TestNG 使 Java 单元测试轻而易举

    http://www.ibm.com/developerworks/cn/java/j-testng/ 在每个现代软件包的构造阶段,测试这一实践都扮演着中心角色.过去那种先编写代码,然后有空的时候再测 ...

  7. 科普文丨Java 单元测试相关概念理清

    对于从业Java的小伙伴来说,"单元测试"这个词你一定不陌生,如果你想要学习 Java 单元测试,下面的内容你一定要看! 近日,实验楼上线了新课<Java 单元测试入门实战& ...

  8. 【Java单元测试】如何进行单元测试、异常测试、参数化测试、超时测试、测试多线程

    Junit单元测试的步骤 (1)新建一个单元测试 (2)选择位置 (3)选择需要测试的方法 (4)是否将Junit 4添加到ClassPath中 (5)自动生成的测试类 (6) 然后就可以编写单元测试 ...

  9. 转载-使用 Feed4JUnit 进行数据与代码分离的 Java 单元测试

    JUnit 是被广泛应用的 Java 单元测试框架,但是它没有很好的提供参数化测试的支持,很多测试人员不得不把测试数据写在程序里或者通过其它方法实现数据与代码的分离,在后续的修改和维护上有诸多限制和不 ...

最新文章

  1. wps解析json数据_通过WordPress HTTP API 获取json内容并解析
  2. linux终端vi退出命令,如何从命令行关闭vim?
  3. 文件的读取流和书写流
  4. Go语言 科学计算库 Gonum 学习1
  5. linux程序设计---序
  6. vsCode ext install 不工作
  7. memlock mysql_mysql配置详解(不断更新)
  8. 产品经理是否应该给 UI 设计师的设计稿提意见?
  9. 将js对象转化为树形结构
  10. 教你一招用 Python Turtle 库画出“精美碎花小清新风格树”,速取代码! | 原力计划...
  11. 【效率技巧】利用TI计算器的程序映射功能 kbdprgm1()~9() 简化GTC程序调试操作
  12. 苹果mac专业的视频转码器:HandBrake
  13. linux系统下怎么安装软件,如何在Linux系统上安装软件
  14. 有限自动机DFA 、 无限自动机NFA
  15. 卖家/消费者如何在淘宝申请人工客服
  16. 小微信贷传统风控模型的痛点
  17. 新任务管理系统YYSchedule-介绍-引擎执行机制及结果回收机制
  18. TiDB大规模删除实践
  19. 怎么做 HDFS 的原地平滑缩容?
  20. 什么叫逐行扫描和隔行扫描

热门文章

  1. c#语言入门 刘老师,c#单元测试实例(学习刘老师视频)
  2. 优化AWS使用成本系列之预留实例(RI)为您提供大幅折扣
  3. 维克森林大学计算机科学专业好不好,2017年维克森林大学计算机科学
  4. Encountered a sharing violation while accessing
  5. pandas 第十二期组队-pandas基础
  6. 关于Chrome浏览器的一些使用技巧
  7. 计算机科学与技术专业学建模嘛,工学学科(基本专业四):计算机科学与技术专业介绍...
  8. dingding钉钉 python接口
  9. Arduino 定时器中断
  10. Digital Ocean 如何使用GitHub学生优惠码