公司对开发人员的单元测试要求比较高,要求分支覆盖率、行覆盖率等要达到60%以上等等。项目中已经集成了jmockit这个功能强大的mock框架,学会使用这个框架势在必行。从第一次写一点不会,到完全可以应付工作要求,期间踩了好多坑,学到了不少东西。下面简单总结一下jmockit这个框架的使用,重点介绍MockUp的使用,因为项目中都采用此种方式模拟方法。

一、框架集成

添加maven依赖

  1. <dependencies>
  2. <!-- jmockit必须写在junit之前 -->
  3. <dependency>
  4. <groupId>org.jmockit</groupId>
  5. <artifactId>jmockit</artifactId>
  6. <version>1.16</version>
  7. <scope>test</scope>
  8. </dependency>
  9.  
  10. <dependency>
  11. <groupId>junit</groupId>
  12. <artifactId>junit</artifactId>
  13. <version>4.12</version>
  14. </dependency>
  15.  
  16. </dependencies>

二、@Mocked模拟方式介绍

@Mocked模拟,由录制、回放、验证三步骤完成,是对某个类的所有实例的所有方法进行完整的模拟方式。

  1. /**
  2. * 被测试类
  3. */
  4. public class App {
  5.  
  6. public String say() {
  7. return "Hello World";
  8. }
  9.  
  10. public String say2(){
  11. return "Hello World 2";
  12. }
  13.  
  14. public static String staticSay() {
  15. return "Still hello world";
  16. }
  17. }
  1. /**
  2. * 测试类
  3. */
  4. public class AppTest {
  5.  
  6. /**
  7. * 针对类及所有实例的的整体模拟,未写录制的方法默认返回0,null等
  8. */
  9. @Mocked
  10. App app;
  11.  
  12. @Test
  13. public void testSay() {
  14.  
  15. //录制,定义被模拟的方法的返回值,可以录制多个行为,写在一个大括号里也可以,多个大括号隔开也可以
  16. new Expectations() {{
  17. app.say();
  18. result = "say";
  19. }};
  20.  
  21. //回放,调用模拟的方法
  22. System.out.println(app.say()); //say
  23. System.out.println(new App().say()); //say
  24. System.out.println(App.staticSay()); //null
  25.  
  26. //验证
  27. new Verifications() {{
  28. //验证say模拟方法被调用,且调用了2次
  29. app.say();
  30. times = 2;
  31.  
  32. //验证staticSay模拟方法被调用,且调用了1次
  33. App.staticSay();
  34. times = 1;
  35. }};
  36.  
  37. }
  38. }

三、@Injectable模拟方式介绍

@Injectable和@Mocked的方式很像,区别是@Injectable仅仅对当前实例进行模拟。 

  1. /**
  2. * 测试类
  3. */
  4. public class AppTest001 {
  5.  
  6. /**
  7. * 仅针对当前实例的整体模拟
  8. */
  9. @Injectable
  10. App app;
  11.  
  12. @Test
  13. public void testSay() {
  14.  
  15. //录制
  16. new Expectations() {{
  17. app.say();
  18. result = "say";
  19. }};
  20.  
  21. //回放
  22. System.out.println(app.say()); //say,模拟值
  23. System.out.println(app.say2()); //null,模拟默认值
  24.  
  25. final App appNew = new App();
  26. System.out.println(appNew.say()); //Hello World,未被模拟,方法实际值
  27. System.out.println(App.staticSay()); //Still hello world,未被模拟,方法实际值
  28.  
  29. //验证
  30. new Verifications() {
  31. {
  32. //验证say模拟方法被调用
  33. app.say();
  34. times = 1;
  35. }
  36. {
  37. appNew.say();
  38. times = 1;
  39. }
  40. {
  41. //验证staticSay模拟方法被调用,且调用了1次
  42. App.staticSay();
  43. times = 1;
  44. }
  45. };
  46.  
  47. }
  48. }

四、Expectations传参,局部模拟

  1. /**
  2. * 测试类
  3. */
  4. public class AppTest002 {
  5.  
  6. @Test
  7. public void testSay() {
  8.  
  9. final App app = new App();
  10.  
  11. //录制,带参数表示局部模拟【针对所有实例的局部模拟,具体到某个方法的模拟,其它方法不模拟】
  12. new Expectations(App.class) {{
  13. app.say();
  14. result = "say";
  15. }};
  16.  
  17. //回放
  18. System.out.println(app.say()); //say,模拟值
  19. System.out.println(app.say2()); //Hello World 2 ,未被模拟,方法实际值
  20. System.out.println(new App().say()); //say,模拟值
  21. System.out.println(App.staticSay()); //Still hello world,未被模拟,方法实际值
  22.  
  23. }
  24. }

五、MockUp局部模拟,可重写原有方法的逻辑,比较灵活,推荐使用

  1. /**
  2. * 测试类
  3. */
  4. public class AppTest003 {
  5.  
  6. @Test
  7. public void testSay() {
  8. //局部模拟【针对所有实例的局部模拟,具体到某个方法的模拟,其它方法不模拟】
  9. new MockUp<App>(App.class){
  10.  
  11. @Mock
  12. String say(){
  13. return "say";
  14. }
  15. };
  16.  
  17. //回放
  18. System.out.println(new App().say()); //say,模拟值
  19. System.out.println(new App().say2()); //Hello World 2,未被模拟,方法实际值
  20. System.out.println(App.staticSay()); //Still hello world,未被模拟,方法实际值
  21.  
  22. }
  23. }

六、MockUp如何模拟私有方法、静态方法、静态块、构造函数等

1.模拟私有属性(实例属性和类属性),MockUp不支持,采用如下方式

  1. //模拟实例的字段
  2. Deencapsulation.setField(Object objectWithField, String fieldName, Object fieldValue)
  3.  
  4. //模拟类的静态字段
  5. Deencapsulation.setField(Class<?> classWithStaticField, String fieldName, Object fieldValue)

2.模拟私有方法,MockUp不支持,采用如下方式

  1. //模拟实例方法,注意参数不能为null,如果要传null请使用带参数类型的另一个重载方法
  2. Deencapsulation.invoke(Object objectWithMethod, String methodName, Object... nonNullArgs)
  3.  
  4. //模拟类方法
  5. Deencapsulation.invoke(Class<?> classWithStaticMethod, String methodName, Object... nonNullArgs)

3.模拟静态方法

和模拟实例方法一样,去掉static即可

4.模拟静态块

  1. //mock静态代码块
  2. @Mock
  3. void $clinit(Invocation invocation){
  4.  
  5. }

5.模拟实例块和构造函数

  1. //mock代码块和构造函数
  2. @Mock
  3. void $init(Invocation invocation) {
  4.  
  5. }

6.模拟接口,MockUp不支持,采用@Capturing

  1. /**
  2. * 被模拟的接口
  3. */
  4. public interface IUserService {
  5.  
  6. String getUserName( );
  7. }
  1. public class UserServiceImpl implements IUserService {
  2.  
  3. @Override
  4. public String getUserName() {
  5. return "Bob";
  6. }
  7. }
  1. /**
  2. * 接口模拟测试
  3. */
  4. public class IUserServiceTest {
  5.  
  6. /**
  7. * MockUp不能mock接口方法,可以用来生成接口实例
  8. */
  9. @Test
  10. public void getUserNameTest001(){
  11. MockUp<IUserService> mockUp = new MockUp<IUserService>(){
  12.  
  13. @Mock
  14. String getUserName( ){
  15. return "Jack";
  16. }
  17. };
  18.  
  19. IUserService obj = new UserServiceImpl();
  20. System.out.println(obj.getUserName()); //Bob,mock失败
  21.  
  22. obj = mockUp.getMockInstance();
  23. System.out.println(obj.getUserName()); //Jack,mockUp生成的实例,和自己写一个接口实现一样
  24.  
  25. obj = new UserServiceImpl();
  26. System.out.println(obj.getUserName()); //Bob,mock失败
  27. }
  28.  
  29. /**
  30. * @Capturing 注解可以实现mock接口,所有实现类的实例均被mock
  31. * @param base
  32. */
  33. @Test
  34. public void getUserNameTest002(@Capturing final IUserService base){
  35. IUserService obj = new UserServiceImpl();
  36. System.out.println(obj.getUserName()); //mock成功,返回模拟默认值null
  37.  
  38. //录制
  39. new Expectations(){
  40. {
  41. base.getUserName();
  42. result = "Jack";
  43. }
  44. };
  45. System.out.println(obj.getUserName()); //Jack
  46.  
  47. obj = new IUserService() {
  48. @Override
  49. public String getUserName() {
  50. return "Alice";
  51. }
  52. };
  53. System.out.println(obj.getUserName()); //Jack
  54. }
  55.  
  56. }

七、MockUp模拟方法中调用原方法

  1. /**
  2. * 模拟方法调用原方法逻辑测试
  3. */
  4. public class JSONObjectTest {
  5.  
  6. @Test
  7. public void getTest(){
  8. JSONObject jsonObject = new JSONObject();
  9. jsonObject.put("a","A");
  10. jsonObject.put("b","B");
  11. jsonObject.put("c","C");
  12. System.out.println(jsonObject.get("a")); //A
  13. System.out.println(jsonObject.get("b")); //B
  14. System.out.println(jsonObject.get("c")); //C
  15. new MockUp<JSONObject>(){
  16. @Mock
  17. Object get(Invocation invocation,Object key){
  18. if("a".equals(key)){
  19. return "aa";
  20. }else{
  21. //调用原逻辑
  22. return invocation.proceed(key);
  23. }
  24. }
  25. };
  26. System.out.println(jsonObject.get("a")); //aa
  27. System.out.println(jsonObject.get("b")); //B
  28. System.out.println(jsonObject.get("c")); //C
  29. }
  30. }

八、MockUp单元测试用例单个跑正常,批量跑失败可能的原因

1.测试方法使用了共享变量,相互影响。

2.在一个测试方法里多次MockUp同一个类,将某个类需要mock的方法均写在一个new MockUp里即可解决,原因未知。

3.在测试方法里的MockUp,在方法结束前,调用一下mockUp.tearDown。

jmockit使用总结-MockUp重点介绍的更多相关文章

  1. 消息队列介绍、RabbitMQ&Redis的重点介绍与简单应用

    消息队列介绍.RabbitMQ&Redis的重点介绍与简单应用 消息队列介绍.RabbitMQ.Redis 一.什么是消息队列 这个概念我们百度Google能查到一大堆文章,所以我就通俗的讲下 ...

  2. .NET:“事务、并发、并发问题、事务隔离级别、锁”小议,重点介绍:“事务隔离级别"如何影响 “锁”?

    备注 我们知道事务的重要性,我们同样知道系统会出现并发,而且,一直在准求高并发,但是多数新手(包括我自己)经常忽略并发问题(更新丢失.脏读.不可重复读.幻读),如何应对并发问题呢?和线程并发控制一样, ...

  3. 各种vpn协议介绍(重点介绍sslvpn的实现方式openvpn)

    vpn介绍:   VIrtual Private Network 虚拟专用网络哪些用户会用vpn?    公司的远程用户(出差.家里),公司的分支机构.idc机房.企业间.FQ常见vpn协议有哪些?  ...

  4. spring框架总结(03)重点介绍(Spring框架的第二种核心掌握)

    1.Spring的AOP编程 什么是AOP?  ----- 在软件行业AOP为Aspect Oriented Programming  也就是面向切面编程,使用AOP编程的好处就是:在不修改源代码的情 ...

  5. 进击的Python【第十一章】:消息队列介绍、RabbitMQ&Redis的重点介绍与简单应用

    消息队列介绍.RabbitMQ.Redis 一.什么是消息队列 这个概念我们百度Google能查到一大堆文章,所以我就通俗的讲下消息队列的基本思路. 还记得原来写过Queue的文章,不管是线程queu ...

  6. java中关于IO流的知识总结(重点介绍文件流的使用)

    今天做软件构造实验一的时候,用到了java文件流的使用,因为之前学的不是很踏实,于是决定今天好好总结一下, 也方便以后的回顾. 首先,要分清IO流中的一些基础划分: 按照数据流的方向不同可以分为:输入 ...

  7. html的特质语义:微格式及其他(重点介绍其中两种)

    今天再次翻开html的书本, 感觉过了个周末似乎生疏了许多, 虽然我是刚接触html的, 但是对于他还是抱有极其大的兴趣的, 所以不爱看书的我, 也开始一遍遍的翻阅着书本, 寻找解决问题的方法, 下面 ...

  8. ISO c++ 14 重点介绍[译]

    原文链接 http://marknelson.us/2014/09/11/highlights-of-iso-c14/ 下面是对你的日常开发有重大影响的C++14新变动,列出了一些示例代码,并讨论何时 ...

  9. 谈谈javascript数组排序方法sort()的使用,重点介绍参数使用及内部机制?

    语法:arrayObject.sort(sortby) 参数sortby可选,规定排序顺序,必须是函数: 注:如果调用该方法时没有使用参数,将按字符编码的顺序进行排序,要实现这一点,首先应把数组的元素 ...

随机推荐

  1. hive Hive 2.0函数大全(中文版)(转)

    转自:https://www.cnblogs.com/MOBIN/p/5618747.html#1 摘要 Hive内部提供了很多函数给开发者使用,包括数学函数,类型转换函数,条件函数,字符函数,聚合函 ...

  2. Windows Automation API 3.0 Overview

    https://www.codemag.com/article/0810042 While general accessibility requirements (such as font color ...

  3. [Algorithm] 21. Merge Two Sorted Lists

    Merge two sorted linked lists and return it as a new list. The new list should be made by splicing t ...

  4. js字符串转换为JSON

    1. json字符串 jsStr =  “{"a":'xxx', "b":'yyy'}” JSON.parse(jsStr); //可以将json字符串转换成j ...

  5. Xamarin.Forms 自定义控件(呈现器和效果)

    Xamarin.Forms 使用目标平台的本机控件呈现用户界面,从而让 Xamarin.Forms 应用程序为每个平台保留了相应的界面外观.凭借效果,无需进行自定义呈现器实现,即可自定义每个平台上的本 ...

  6. 04-树5 Root of AVL Tree (25 分)

    An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child sub ...

  7. 拼图验证码 js,vue

    可查看github网站

  8. ELK实时日志分析平台环境部署,以及可视化展示

    ELK是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是开源软件.新增了一个FileBeat,它是一个轻量级的日志收集处理工具(Agent) ...

  9. 深入分析Synchronized原理(阿里面试题)

    还有一篇 讲解lock的实现原理,参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理以及如何 获取锁AQS算法 (百度-美团) 记得开始学习Java的时候,一遇到多线程 ...

  10. mysql一对多表结构,查询一的信息的同时统计多的数量

    res_resource_catalog表对于res_info_item表是一对多, 查询res_resource_catalog信息的同时,统计res_info_item中该条res_resourc ...