前面一篇说到了Mockito的各种功能,可以帮助我们在编写测试用例的时候模拟对象的各种行为,但是Mockito对于一些场景还是无法满足,比方说静态方法,私有方法(不过一般正常的单元测试很少去mock私有方法),构造方法等

github上关于mockito不支持的地方给了如下说明:

Do not mock types you don’t own
Don’t mock value objects
Don’t mock everything
Show some love with your tests

在FAQ中,也写了mockito的其他一些问题

Mockito 2.x specific limitations

  • Requires Java 6+
  • Cannot mock static methods
  • Cannot mock constructors
  • Cannot mock equals(), hashCode(). Firstly, you should not mock those methods. Secondly, Mockito defines and depends upon a specific implementation of these methods. Redefining them might break Mockito.
  • Mocking is only possible on VMs that are supported by Objenesis. Don’t worry, most VMs should work just fine.
  • Spying on real methods where real implementation references outer Class via OuterClass.this is impossible. Don’t * worry, this is extremely rare case.

Can I mock static methods?
No. Mockito prefers object orientation and dependency injection over static, procedural code that is hard to understand & change. If you deal with scary legacy code you can use JMockit or Powermock to mock static methods.

Can I mock private methods?
No. From the standpoint of testing… private methods don’t exist. More about private methods here.

Can I verify toString()?
No. You can stub it, though. Verification of toString() is not implemented mainly because:

When debugging, IDE calls toString() on objects to print local variables and their content, etc. After debugging, the verification of toString() will most likely fail.
toString() is used for logging or during string concatenation. Those invocations are usually irrelevant but they will change the outcome of verification.

对于上述Mockito不能实现的功能,PowerMock可以满足我们的需求。PowerMock 也是一个单元测试模拟框架,它是在其它单元测试模拟框架的基础上做出的扩展。通过提供定制的类加载器以及一些字节码篡改技巧的应用,PowerMock 实现了对静态方法、构造方法、私有方法以及 Final 方法的Mock支持等强大的功能。目前,PowerMock 仅支持 EasyMock 和 Mockito。

1.依赖jar包

maven项目需要引入如下jar:

<dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>1.7.4</version><scope>test</scope>
</dependency>
<dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito</artifactId><version>1.7.4</version><scope>test</scope>
</dependency>
2.mock静态方法
public class CommonUtils {public static String getUUID() {String uuid = UUID.randomUUID().toString();uuid = uuid.replace("-", "");return uuid;}
}
public class UUIDTest {public String getUUId(){return CommonUtils.getUUID();}
}

两个类,一个是静态工具类,一个是调用工具类的测试类
mock静态方法的时候就需要使用PowerMockRunner并且添加@PrepareForTest注解,还需要调用对应的mockStatic方法mock静态方法所在的类

RunWith(PowerMockRunner.class)
@PrepareForTest({CommonUtils.class})
public class CommonUtilsTest {@Testpublic void test() throws ParseException {PowerMockito.mockStatic(CommonUtils.class);PowerMockito.when(CommonUtils.getUUID()).thenReturn("12345678910111111");UUIDTest test = new UUIDTest();Assert.assertEquals("12345678910111111",test.getUUId());}
}
3.mock私有及final方法
public class TargetClass {public String mockPrivateFunc(int i) {return privateFunc(i + "");}private final String privateFunc(String i) {return "0";}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({TargetClass.class})
public class MockTest {@Testpublic void testMockPrivateFunc() throws Exception {TargetClass targetClass = PowerMockito.spy(new TargetClass());PowerMockito.when(targetClass,"privateFunc",anyString()).thenReturn("test");String realResult = targetClass.mockPrivateFunc(1);Assert.assertEquals("test", realResult);}
}
4.mock构造方法
public class User {private String username;private String password;public User(String username, String password) {this.username = username;this.password = password;}@Overridepublic String toString() {return username+","+password;}
}
@RunWith(PowerMockRunner.class)
//注意下这里添加的是调用构造方法的类而不是User类
@PrepareForTest({UserService.class})
public class UserServiceTest {@Testpublic void saveUser() throws Exception {String username = "mock姓名";String password = "aaa";User user = new User(username, password);UserService userService = new UserService();PowerMockito.whenNew(User.class).withAnyArguments().thenReturn(user);userService.saveUser("真实姓名","123");}

总结下之里的规律:
如果需要使用PowerMock来mock构造方法,私有方法,final方法和静态方法,那么都需要使用PowerMockRunner和
@PrepareForTest注解

  • 当使用mock构造方法时,注解@PrepareForTest里写的类是需要mock的新对象生成的代码所在的类。
  • 当需要mock系统类的静态方法的时候,注解里写的类是需要调用系统方法所在的类
  • 当需要mock final方法,静态方法,私有方法的时候,注解@PrepareForTest里写的类是对应方法所在的类
5.Field

@InjectMocks和@Mock注解配合使用可以帮我们做自动注入,在编写单元测试时也可以使用SpringJUnit4ClassRunner来帮助我们做一些属性的注入和自动装配。
但是这样在单机环境下由于spring容器在启动的时候会自动完成很多初始化工作,一来比较耗时,二来会去连接一些其他中间件比方说配置中心等,单机下就会出现异常

那么我们就需要PowerMock的field方法来帮助我们做一些装配的工作

        OperateButtonService operateButtonService = new OperateButtonService();Map<String, AbstractGetOperateButton> operateButtonMap = new HashMap<>();operateButtonMap.put("CALL_HOTEL", callHotel); operateButtonMap.put("COMMENT", comment);operateButtonMap.put("RESERVE_AGAIN", reserveAgain);PowerMockito.field(OperateButtonService.class, "operateButtonMap").set(operateButtonService, operateButtonMap);

对operateButtonService的operateButtonMap字段进行赋值,底层原理也是反射,不过powermock帮我们封装了下,更加容易使用,对于私有变量和静态变量都可以进行赋值(常量不行)

6.mock静态void方法 (2020/5/15补充)

使用PowerMock mock静态void方法,并且通过VerifyStatic验证调用次数

public class CommonUtils {public static void log(){System.out.println("日志记录");}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({CommonUtils.class})
public class CommonUtilsTest {@Testpublic void testStaticVoidMethod() throws ParseException {PowerMockito.mockStatic(CommonUtils.class);PowerMockito.doNothing().when(CommonUtils.class);//这里不是对静态方法的调用,而是指定了Stub的void方法CommonUtils.log();//这里才是对静态方法的调用CommonUtils.log();CommonUtils.log();//验证的时候和上面同理PowerMockito.verifyStatic(CommonUtils.class, Mockito.times(2));CommonUtils.log();}
}

参考链接:
https://stackoverflow.com/questions/18466198/how-to-verify-static-void-method-has-been-called-with-power-mockito

7.其他注解的功能

@PowerMockRunnerDelegate

如果在使用了PowerMockRunner之后还想使用Spring容器的功能,那么我们就需要spring的runner

@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)

powermock使用了自定义的classloader来解决mock静态方法与私有方法的问题,因此其会为加了PrepareForTest注解的类生成对应的classloader来加载用到的类,这样就可能会导致其与系统的classloader加载了相同的类,导致类型转换失败,我们可以使用@PowerMockIgnore注解告诉powermock放弃加载指定的这些类

@PowerMockIgnore({"javax.management.*", "javax.net.ssl.*"})

这也是powermock2.0.0与1.x版本重大不一样的地方

@SuppressStaticInitializationFor

在单机环境下(无法使用任何外部的资源),因为代码中我们可能会在static静态块或者常量里做一些初始化的操作,比方说提前生成Redis操作的管理类等,此时我们就需要避免这些初始化操作,因为尝试调用其他服务资源都会失败

@SuppressStaticInitializationFor(“com.chenpp.RedisManager”)
忽略指定类的静态初始化, 包括static{}静态代码块和static变量的初始化

PowerMock简单原理
@RunWith(PowerMockRunner.class)
public class UserServiceTest {@Test@PrepareForTest({UserService.class})public void testSaveUser() throws Exception {String username = "mock姓名";String password = "aaa";User user = new User(username, password);Long current = System.currentTimeMillis();UserService userService = new UserService();PowerMockito.whenNew(User.class).withAnyArguments().thenReturn(user);userService.saveUser("真实姓名", "123");System.out.println("testSaveUser:" + userService.getClass().getClassLoader());System.out.println("testSaveUser:" + user.getClass().getClassLoader());System.out.println("testSaveUser:" + System.class.getClassLoader());}@Testpublic void testUser() throws Exception {UserService userService = Mockito.mock(UserService.class, RETURNS_DEEP_STUBS);Mockito.when(userService.getUser().show()).thenReturn("mock test");Assert.assertEquals("mock test", userService.getUser().show());System.out.println("testUser:" + userService.getClass().getClassLoader());System.out.println("testUser:" + User.class.getClassLoader());}
}


@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {@Testpublic void testUser() throws Exception {UserService userService = Mockito.mock(UserService.class, RETURNS_DEEP_STUBS);Mockito.when(userService.getUser().show()).thenReturn("mock test");Assert.assertEquals("mock test", userService.getUser().show());System.out.println("testUser:" + userService.getClass().getClassLoader());System.out.println("testUser:" + User.class.getClassLoader());}
}


当在某个测试类上使用PowerMockRunner,那么在运行测试用例时,会创建一个新的org.powermock.core.classloader.MockClassLoader类加载器,然后使用该类加载器加载测试用例使用到的类(系统类除外)

PowerMock会根据你的mock要求,去修改写在注解@PrepareForTest里的class文件(当前测试类会自动加入注解中),以满足特殊的mock需求。例如:去除final方法的final标识,在静态方法的最前面加入自己的虚拟实现等。

如果需要mock的是系统类的final方法和静态方法,PowerMock不会直接修改系统类的class文件,而是修改调用系统类的class文件,以满足mock需求。

单元测试之更强大的powermock相关推荐

  1. Java单元测试之模拟利器-使用PowerMock进行Mock测试

    首页 国产Linux Linux命令 openSUSE ArchLinux Slackware FreeBSD Ubuntu CentOS Fedora Debian PHP教程 在线教程 登录 注册 ...

  2. 单元测试之关于JaCoCo和PowerMock冲突导致类覆盖率为0的问题

    在使用Mockito和PowerMock写单测的时候发现,如果使用了PowerMock的@PrepareForTest注解,JaCoCo在统计代码覆盖率的时候就会忽略注解@PrepareForTest ...

  3. Android 单元测试之Robolectric

    前言 在博客Android 单元测试之PowerMockito,主要介绍PowerMockito的使用和对Java测试用例的强大支持.但对于Android app开发来说,写起单元测试很痛苦:一方面单 ...

  4. Java单元测试之JUnit4详解

    2019独角兽企业重金招聘Python工程师标准>>> Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @B ...

  5. Java基础学习总结(24)——Java单元测试之JUnit4详解

    Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @BeforeClass 全局只会执行一次,而且是第一个运行 @Before  ...

  6. 艾伟_转载:单元测试之道(使用NUnit)

    首先来看下面几个场景你是否熟悉 1.你正在开发一个系统,你不断地编码-编译-调试-编码-编译-调试--终于,你负责的功能模块从上到下全部完成且编译通过!你长出一口气,怀着激动而又忐忑的心情点击界面上的 ...

  7. Android单元测试之Local unit tests(上)

    Android单元测试之Local unit tests(上) 简介 本地单元测试 JUnit 4 添加依赖 测试例子 结论 Mockito 添加依赖 测试例子-mock基本使用 测试例子-mock与 ...

  8. Android 单元测试之Mockito

    在博客Android 单元测试之JUnit4中,我们简单地介绍了:什么是单元测试,为什么要用单元测试,并展示了一个简单的单元测试例子.在文章中,我们只是展示了对有返回类型的目标public方法进行了单 ...

  9. 单元测试之道一:NUnit基础

    一.单元测试的概述 1.单元测试是开发编写的一小段代码,用于检测被测代码的一个很小的,很明确的功能是否正确.通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为.执行单元测试是 ...

最新文章

  1. ToDictionary的用法
  2. arcgis api for JavaScript _跨域请求
  3. 升级oracle spu,Oracle 2017改变:新补丁更新(RU和RUR),新的版本(Release 18和19)
  4. 密码技术应用--RSA文件签名验签
  5. seL4 microkernel学习资料
  6. flutter制作博客展示平台,现已支持 Web、macOS 应用、Android 和 iOS
  7. 数据可视化实现技术(canvas/svg/webGL)
  8. 甘肃省事业单位公考招聘考试权威复习资料---GIS专业全真模拟题(一)
  9. R语言绘图底层系统之Grid包
  10. [LintCode] 翻转二叉树
  11. github上看到的springboot做的后台管理系统,bootdo,适合大家学习入门
  12. 【Git】<分布式版本控制系统>版本控制器Git概述
  13. Longest Continuous 1
  14. 《今日简史》一、旧故事已然崩坏,新故事尚未构建
  15. rk3128-android7-定频
  16. midjourney使用方法保姆级注册教程AI绘画工具关键词
  17. 除了中国知网和谷歌文学还有哪些好的有权威的资源站?
  18. ElasticSearch 亿级数据检索深度优化
  19. codeforces1064E Dwarves, Hats and Extrasensory Abilities
  20. 如何轻松安装 Debian Linux 系统

热门文章

  1. JAVA多线程和并发
  2. python try语句各种格式输出_python如何写try语句
  3. koa2 mysql增删改查_react+koa2+mysql零门槛的全栈体验,附上完整项目分享
  4. 200917阶段一C++双向链表模板
  5. 【小题目】输入三个数字,获取三个数字中的最小值
  6. Linux watch 监控系统状态
  7. 基于DDD的.NET开发框架 - ABP模块设计
  8. 大数据时代的电信运营商的机遇
  9. 2021HDU多校6 - 7028 Decomposition(构造)
  10. 牛客 - Colorful Tree(dfs序+LCA)