当前位置:网站首页>单元测试系统化讲解之PowerMock
单元测试系统化讲解之PowerMock
2022-08-11 08:56:00 【暗余】
单元测试系统化讲解之PowerMock
- 什么是PowerMock
- Mock局部变量
- Mock静态方法
- Mock final 修饰的方法
- Mock私有方法
- Verify的使用
- Mock不同的构造函数
- Parameters Matcher接口的使用
- Answer接口的使用
- Spy的使用
一、什么是PowerMock
1.1 前述
本次讲解的PowerMock是单元测试的进阶技术框架;所以学习PowerMock中,博主假设你们已经满足如下条件:
- 知道什么是单元测试
- 明白Junit/Mockito相关的一些使用或知识
- 想要了解单元测试相关的一些技术
PowerMock是什么?
- PowerMock是一个扩展了其它如EasyMock等mock框架的、功能更加强大的框架。PowerMock使用一个自定义类加载器和字节码操作来模拟静态方法、构造方法、final类和方法、私有方法、去除静态初始化器等等。
- 通过使用自定义的类加载器,简化采用的IDE或持续集成服务器不需要做任何改变。
- 熟悉PowerMock支持的mock框架的开发人员会发现PowerMock很容易使用,因为对于静态方法和构造器来说,整个的期望API是一样的。
- PowerMock旨在用少量的方法和注解扩展现有的API来实现额外的功能。目前PowerMock支持EasyMock和Mockito。
- PowerMock是基于其他Mock框架,做的增强。所以不能单独使用它。
PowerMock解决了哪些痛点?
- 现如今比较流行的Mock工具如jMock,EasyMock,Mockito等都有一个共同的缺点:不能mock静态、final、私有方法等。而PowerMock能够完美的弥补以上三个Mock工具的不足。
- 能够mock方法内
new 出来的对象
注意:为什么总说PowerMock尽量少用?
- PowerMock是一个非常强大且有用的工具。 但是,仅在绝对必要时才应使用它,因为它会对测试执行时间产生巨大影响。
- 缺乏API知识,过于严格的可见性和直接的静态方法调用。
- 如果您发现测试代码库中充满了PowerMock的用法,建议您尝试上述方法以摆脱它们。
- 如果单元测试里面出现了太多的不必要的PowerMockito使用,那么这个单元测试的质量是有待考量的,特别是一些私有方法尽量不用使用PowerMockito,善PowerMockito,尽量提高单元测试的真实性.
1.2 PowerMock快速入门
PowerMock有两个重要的注解,分别是:
@RunWith(PowerMockRunner.class) @PrepareForTest( { YourClassWithEgStaticMethod.class })使用了
@PrepareForTest注解,能够使用PowerMock的强大功能(mock静态、final、私有方法等);需要注意的是,它不能单独生效,我们必须要同时加上@RunWith(PowerMockRunner.class)使用PowerMock的依赖项:
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>1.6.5</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>1.6.5</version> <scope>test</scope> </dependency>开始测试:
编写被测试的类:
public class UserService{ private UserDao userDao; public UserService(UserDao userDao){ this.userDao = userDao; } public int querUserCount(){ return userDao.getCount(); } public void saveUser(User user){ userDao.insertUser(user); } }测试类:
public class UserServiceTest{ private UserService userService; @Before public void setUp(){ userService = new UserService(new UserDao()); } @Mock private UserDao userDao; @Test public void testQueryUserCountWithPowerMock(){ UserDao uDao = PowerMockito.mock(UserDao.class); PowerMockito.when(uDao.getCount()).thenReturn(10); UserService service = new UserService(uDao); int result = service.queryUserCount(); assertEquals(10,result); } @Test public void testQueryUserCountWithMockito(){ MockitoAnnotations.initMocks(this); UserService service = new UserService(userDao); Mockito.when(userDao.getCount()).thenReturn(10); int result = service.queryUserCount(); assertEquals(10,result); } @Test public void testSaveUserWIthJunit(){ try{ userService.saveUser(new User()); fail("should not process to here"); }catch (Exception e){ assertTrue(e instanceof UnsupportedOperationException); } } }
代码中展示了Junit、mockito、powermock的三种测试方式。
二、PowerMock实战演示
2.1 在方法内使用局部变量
- 被测试类:
public class UserService{
public int queryUserCount(){
UserDao userDao = new UserDao();
return userDao.getCount();
}
public void saveUser(User user){
UserDao userDao = new userDao();
userDao.insertUser(user);
}
}
- 测试类
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest{
@Test
public void tesetQueryUserCount(){
try{
UserService userService = new UserService();
UserDao userDao = mock(UserDao.class);
whenNew(UserDao.class).withNoArguments().thenReturn(userDao);
doReturn(10).when(userDao).getCount();
int result = userService.queryUserCount();
assertEquals(10,result);
}catch(Throwable e){
fail();
}
}
@Test
public void testSaveUser(){
try{
User user = new User();
UserService userService = new UserService();
UserDao userDao = mock(UserDao.class);
whenNew(UserDao.class).withAnyArguments().thenReturn(userDao);
userService.saveUser(user);
Mockito.verify(userDao,Mockito.times(1)).insertUser(user);
}catch(Throwable e){
fail();
}
}
}
2.2 Mock静态方法
- 静态Dao层
public class UserDao{
public static int getCount(){
throw new UnsupportedOperationException();
}
public static void insertUser(User user){
throw new UnsupportedOperationException();
}
}
- 被测试的Service层
public class UserService{
public int queryUserCount(){
return UserDao.getCount();
}
public void saveUser(User user){
UserDao userDao = new UserDao();
UserDao.insertUser(user);
}
}
- 测试类
@RunWith(PowerMockRunner.class)
@PrepareForTest({
UserService.class,UserDao.class})
public class UserServiceTest{
@Test
public void testQueryUserCount() throws Exception{
PowerMockito.mockStatic(UserDao.class);
PowerMockito.when(UserDao.getCount()).thenReturn(10);
UserService userService = new UserService();
int result = userService.queryUserCount();
assertEquals(10,result);
}
@Test
public void testSaveUser() throws Exception{
mockStatic(UserDao.class);
User user = new User();
doNoting().when(UserDao.class);
UserService userService = new UserService();
userService.saveUser(user);
PowerMockito.verifyStatic();
}
}
2.3 final 修饰的类
- final 类
final public class UserDao{
public int getCount(){
throw new UnsupportedOperationException();
}
public void insertUser(User user){
throw new UnsupportedOperationException();
}
}
- Service类
public class UserService{
private UserDao userDao;
public UserService(UserDao userDao){
this.userDao = userDao;
}
public int queryUserCount(){
return userDao.getCount();
}
public void saveUser(User suer){
userDao.insertUser(user);
}
}
- 测试类
@RunWith(PowerMockRunner.class)
@PrepareForTest({
UserService.class,UserDao.class})
public class UserServiceTest{
@Test
public void testQueryUserCountWithPowerMock() throws Exception{
UserDao uDao = PowerMockito.mock(UserDao.class);
System.out.println(uDao.getClass());
PowerMockito.when(uDao.getCount()).thenReturn(10);
UserService userService = new UserService(uDao);
int result = userService.queryUserCount();
assertEquals(10,result);
}
}
2.4 verify的使用
- UserDao类
public class UserDao{
public int getCount(User user){
throw new UnsupportedOperationException();
}
public void updateUser(User user){
throw new UnsupportedOperationException();
}
public void insertUser(User user){
throw new UnsupportedOperationException();
}
}
- UserService
public class UserService{
public void saveOrUpdate(User user){
UserDao userDao = new UserDao();
if(userDao.getCount(user) > 0){
userDao.updateUser(user);
}else{
userDao.insertUser(user);
}
}
}
- UserServiceTest
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserSErviceTest{
@Test
public void testSaveOrUpdateWillUseNewJoiner() throw Exception {
User user = PowerMockito.mock(User.class);
UserDao userDao = PowerMockito.mock(UserDao.class);
PowerMockito.whenNew(UserDao.class).withAnyArguments().thenReturn(user);
PowerMockito.when(userDao.getCount(user)).thenReturn(0);
UserService userService = new UserService();
userService.saveOrUpdate(user);
}
@Test
public void testSaveOrUpdateWillUseUpdateOriginal() throw Exception{
User user = PowerMockito.mock(User.class);
UserDao userDao = PowerMockito.mock(userDao.class);
PowerMockito.whenNew(UserDao.class).withAnyArguments().thenRetuurn(userDao);
PowerMockito.when(userDao.getCount(user)).thenReturn(1);
UserService userService = new UserService();
userService.saveOrUpdate(user);
Mockito.verify(userDao).insertUser(user);
Mockito.verify(userDao,Mockito.never()).updateUser(user); // never表示一次都不会执行
}
}
verify有很多方法,同学们可以看看源码或者官方文档来进行使用和练习;
2.5 Mock 构造函数
- UserDao
public class UserDao{
private String username;
private String password;
public UserDao(String username,String password){
this.username = username;
this.password = password;
}
public void insert(){
throw new UnsupportedOperationException();
}
}
- UserService
public class UserService{
public void save(String username, String password){
UserDao userDao = new UserDao(username,password);
userDao.insert();
}
}
- UserServiceTest
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest{
@Test
public void testSave() throws Exception(){
UserDao userDao = PowerMockito.mock(UserDao.class);
String username = "anyu";
String passsword = "lyle";
PowerMocktio.whenNew(UserDao.class).withArguments(username,passsword).thenReturn(userDao);
PowerMockito.doNoting().when(userDao).insert();
UserService userService = new UserService();
Mockito.verify(userDao).insert();
}
}
2.6 Arguments Matcher的使用
- UserDao
public class UserDao{
public String queryByName(String username){
throw new UnsupportedOperationException();
}
}
- UserService
public class UserService{
public String find(String name){
UserDao userDao = new UserDao();
return userDao.queryByName(name);
}
}
- UserServiceTest
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest{
@Test
public void testFind() throws Exception{
UserDao userDao = PowerMockito.mock(UserDao.class);
PowerMockito.whenNew(UserDao.class).withAnyArguments().thenReturn(userDao);
PowerMockito.when(userDao.queryByName("lyle")).thenReturn("anyu");
UserService service= UserService();
String result = service.find("lyle");
assertEquals("anyu",result);
PowerMockito.when(userDao.queryByName("Jacky")).thenReturn("anyu");
UserService service= UserService();
String result = service.find("lyle");
assertEquals("anyu",result);
PowerMockito.when(userDao.queryByName("Tommy")).thenReturn("anyu");
UserService service= UserService();
String result = service.find("lyle");
assertEquals("anyu",result);
}
// 使用match
@Test
public void testFindWithMatcher() throw Exception{
UserDao userDao = PowerMockito.mock(UserDao.class);
PowerMockito.whenNew(UserDao.class).withAnyArguments().thenReturn(userDao); //CSDN:暗余
PowerMockito.when(userDao.queryByName(Matchers.argThat(new MyArgumentMatcher()))).thenReturn("anyu");
UserService service = new UserService();
assertEquals("lyle",service.find("lyle"));
assertEquals("lyle",service.find("Jacky"));
assertEquals("lyle",service.find("Van"));
assertEquals("lyle",service.find("Tony"));
}
static class MyargumentMatcher extends ArgumentMatcher<String>{
@Override
public boolean matches(Object o){
String arg = (String) o;
switch(arg){
case "lyle":
case "Jacky":
case "Van":
case "Tony":
return true;
default:
return false;
}
}
}
}
2.7 Answer 接口的使用
- UserDao
public class UserDao{
public String queryByName(String username){
throw new UnsupportedOperationException();
}
}
- UserService
public class UserService{
public String find(String name){
UserDao userDao = new UserDao();
return userDao.queryByName(name);
}
}
- UserServiceTest
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest{
@Test
public void testFindWithAnswer() throw Exception{
UserDao userDao = PowerMockito.mock(UserDao.class);
PowerMockito.whenNew(UserDao.class).withAnyArguments().thenReturn(userDao);
PowerMockito.when(userDao.queryByName(Mockito.anyString())).then(invocation -> {
String arg = (String) invocation.getArguments()[0];
switch(arg){
case "lyle":
return "I am csdn 暗余";
case "Alex":
return "I am alex";
default:
throw new RuntimeException("Not support "+ arg);
}
});
UserService service = new UserService();
assertEquals("I am casn 暗余",service.find("lyle"));
assertEquals("I am alex", service.find("Alex"));
}
}
2.8 Spy的使用
- UserService
public class UserService{
public void foo(){
log();
}
private void log(){
System.out.println("I am console log.");
}
public boolean exist(String username){
return checkExist(username);
}
private boolean checkExist(String username){
throw new UnsupportedOperationException();
}
}
使用spy会调用真实的方法:

如果使用了when函数指定了对应方法的请求参数,当传入对应的请求参数时会调用mock的方法不会打印任何内容;

而不满足条件,则会继续执行真实的方法:

测试私有方法:
public class UserServiceTest{
@Test
public void testCheck() throws Exception{
UserService userService = PowerMockito.apy(new UserService());
PowerMockito.doReturn(true).when(userService,"checkExist","lyle");
assertTrue(userService.exist("lyle"));
userService.exist("other");
}
}
写文不易,觉得不错点个赞再走吧~ ^^
边栏推荐
猜你喜欢

万字长文带你了解多态的底层原理,这一篇就够了

Redis 只会用缓存?20种妙用让同事直呼牛X(荣耀典藏版)

tensorflow 基础操作1(tensor 基本属性 , 维度变换,数学运算)

中国电子学会五级考点详解(一)-string类型字符串

通过Xshell连接Vagrant创建的虚拟机

What should I do if the mysql data query causes the cup to be full because the query time span is too large

小目标检测3_注意力机制_Self-Attention

Typescript基本类型---上篇

Notable NFT development trends in 2022

Rust从入门到精通06-函数
随机推荐
Go 语言的诞生
Kotlin算法入门求回文数数算法优化二数字生成规则
一根网线两台电脑传输文件
轻量级网络(一):MobileNet V1,V2, V3系列
STM32之串口传输结构体
IQUNIX A80 exploring TTC金粉 初体验
halcon实例
Getting Started with Kotlin Algorithms Calculating Prime Factors
MySql事务
mysql添加用户以及设置权限
Filesystem Hierarchy Standard
如何通过 IDEA 数据库管理工具连接 TDengine?
MySQL性能调优,必须掌握这一个工具!!!(1分钟系列)
无代码平台助力中山医院搭建“智慧化管理体系”,实现智慧医疗
基于consul的注册发现的微服务架构迁移到servicemesh
idea 方法注释:自定义修改method的return和params,void不显示
用 Antlr 重构脚本解释器
redis模拟面试
Detailed Explanation of the Level 5 Test Center of the Chinese Institute of Electronics (1)-string type string
Rust从入门到精通06-函数