PowerMock测试框架详解
摘要
本文介绍了Mock背景,常见的单元测试场景以及对应的测试方法,最后简单介绍了powermockit测试框架。理解本文内容,会带你入门java单元测试,其中第一章、第二章需要重点理解。
一、背景
Mock是什么?
Mock(模拟的)是一种隔离测试类功能的方法。例如:mock测试不需要真实连接数据库,或读取配置文件,或连接服务器。mock对象模拟真实服务,mock对象会返回与传递给它的某个虚拟输入相对应的虚拟数据。
为什么要使用Mock?
单元测试重点在于验证代码逻辑以及结果是否正确,但是在测试过程中经常会遇到如下痛点问题:
- 接口之间的相互依赖
- 第三方接口调用,例如连接数据库,连接https等
使用Mock框架可以模拟出外部依赖,只注重测试代码逻辑、验证代码结果,满足测试真实目的。
Mock框架使用流程
创建 外部依赖 的
Mock
对象, 然后将Mock
对象注入到 测试类 中;执行 测试代码;
校验 测试代码 是否执行正确。
本文所需的maven依赖
<scope>test</scope>表示仅作用于测试目录。默认作用于范围compile表示被依赖项目需要参与当前项目的编译,包含测试目录。
mvn -Dmaven.test.skip clean install 方式可以不编译测试用例
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope>
</dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>2.0.2</version><scope>test</scope>
</dependency>
<dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito2</artifactId><version>2.0.2</version><scope>test</scope>
</dependency>
二、单元测试待测内容有哪些分类?
2.1 无框架java代码
使用Junit单元测试包进行测试。
1.被测试类中方法无其他依赖
1)普通无依赖静态方法
静态方法无其他依赖类,可以直接调用方法计算结果。只需要构造期望数据,计算实际数据,最后比对数据。
代码示例:
被测试类:
public class MyMath {public static int add(int num1,int num2) {return num1 + num2;}
}
测试代码:
//测试静态方法@Testpublic void testStaticAdd() {int num1 = 1;int num2 = 1;int expect = 2;int real = MyMath.add(num1, num2);Assert.assertEquals(expect, real);}
2)非静态无依赖方法
代码示例:
被测试类:
public class MyMath {public int multi(int num1,int num2) {return num1 * num2;}
}
测试代码:
@Testpublic void testMulti() {int num1 = 2;int num2 = 3;int expect = 6;int real = new MyMath().multi(num1, num2);Assert.assertEquals(expect, real);}
2.被测试类中方法存在其他可实现依赖
存在其他可实现依赖时,和2.1测试思路一致。
代码示例:
被测试类:
public class MyRectangle {int height;int width;MyMath myMath = new MyMath();public void setHeight(int height) {this.height = height;}public void setWidth(int width) {this.width = width;}public int getArea() {return myMath.multi(width, height);}public int getPerimeter() {return myMath.multi(2, MyMath.add(width, height));}
}
测试代码:
@Testpublic void testRectangleGetArea() {int width = 2;int height = 3;MyRectangle rectangle = new MyRectangle();rectangle.setWidth(width);rectangle.setHeight(height);int expectedArea = 6;int realArea = rectangle.getArea();Assert.assertEquals(expectedArea, realArea);}
3..被测试类存在难以实现的其他依赖
使用Mock框架,构造出虚拟的依赖,如有需要,可对虚拟依赖进行打桩以满足测试要求。
打桩:用来代替依赖的代码,或未实现的代码。对构造出的虚拟对象,定制其行为,以满足测试逻辑要求。
测试service层代码举例:
service层代码:需要依赖dao层类从数据库获取数据。
public class RectangleService {RectangleDao rectangleDao = new RectangleDao();//rectangleDao未开发完成public int getRectangleAreaById(String id) {MyRectangle myRectangle = rectangleDao.getRectangleById(id);return myRectangle.getArea();}
}
Dao层代码:dao层代码未开发或需要连接数据库,不好操作。
public class RectangleDao {public MyRectangle getRectangleById(String id) {//代码未开发return new MyRectangle();}
}
测试用例代码:mock dao层对象,并通过打桩方式定制其行为。
方式1:将mock的rectangleDao对象,通过java反射方式设置到rectangleService对象中
@RunWith(PowerMockRunner.class)
public class RectangleServiceTest {/*** 此处仅测试RectangleService类代码逻辑,因此该类的依赖需要mock出来,并打桩(自定义对象的行为)*/@Testpublic void testRectangleService() throws Exception{//构造service内部依赖的rectangleDao对象,RectangleDao rectangleDao = PowerMockito.mock(RectangleDao.class);PowerMockito.when(rectangleDao.getRectangleById("1")).thenReturn(new MyRectangle(2,3));//通过反射的方式,将mock出来的rectangleDao配置到rectangleService中RectangleService rectangleService = new RectangleService();Field field = rectangleService.getClass().getDeclaredField("rectangleDao");field.setAccessible(true);field.set(rectangleService, rectangleDao);//构造期望数据,计算实际数据,比对两者MyRectangle myRectangle = new MyRectangle(2,3);int expectedArea = myRectangle.getArea();int actualArea = rectangleService.getRectangleAreaById("1");Assert.assertEquals(expectedArea, actualArea);}
}
方式2:将mock的rectangleDao对象,通过注解的方式设置到rectangleService对象中
Note:
- @Mock注解:创建一个Mock对象。
- @InjectMocks注解:创建一个实例对象,将其他用@Mock、@Spy注解创建的对象注入到用该实例中。
- @Before注解:junit中的注解,表示每一个@Test注解测试用例执行前,都会执行一遍。
/**
* 此处仅测试RectangleService类代码逻辑,因此该类的依赖需要mock出来,并打桩(自定义对象的行为)
*/
@RunWith(PowerMockRunner.class)
public class RectangleServiceTest {@InjectMocks //将其他用@Mock(或@Spy)注解创建的对象设置到下面对象中RectangleService rectangleService;//创建bean(类似new RectangleService)@MockRectangleDao rectangleDao;@Beforepublic void setUp() {MockitoAnnotations.initMocks(this);//初始化上面@Mock和@InjectMocks标注对象}@Testpublic void testRectangleService() throws Exception{//打桩PowerMockito.when(rectangleDao.getRectangleById("1")).thenReturn(new MyRectangle(2,3));//构造期望数据,计算实际数据,比对两者MyRectangle myRectangle = new MyRectangle(2,3);int expectedArea = myRectangle.getArea();//调用实际数据并对比int actualArea = rectangleService.getRectangleAreaById("1");Assert.assertEquals(expectedArea, actualArea);}
}
显然,第2种方式更加方便,尤其是被测试类中依赖许多其他对象时,注解方式更加高效。
2.2 依赖框架的java代码
- spring-boot框架中,类的依赖通过@Autowired注入,而非new创建,但两者本质一样。在springboot框架中,同样可以使用2.3中的方式进行单元测试。
- 此外,在springboot框架中还可以使用spring-boot-test包进行单元测试。
1.被测试类不存在难以实现的其他依赖
可直接使用junit测试包对代码逻辑进行测试,参考2.1章节。
2.被测试类存在难以实现的其他依赖
方式1:类似2.1.3,直接使用powermock框架对代码进行测试
代码示例:
service层:依赖dao层方法。
和无框架代码区别在于,springboot框架内开发的bean都交给spring IOC容器管理,使用时直接注入而非new对象,但两者本质一样。
@Service
public class RectangleService {@AutowiredRectangleDao rectangleDao;//rectangleDao未开发完成public int getRectangleAreaById(String id) {MyRectangle myRectangle = rectangleDao.getRectangleById(id);return myRectangle.getArea();}
}
dao层:和数据库交互。
@Component
public class RectangleDao {public MyRectangle getRectangleById(String id) {//代码未开发return new MyRectangle();}
}
测试用例:
@RunWith(PowerMockRunner.class)
public class RectangleServiceTest {@InjectMocks //将其他用@Mock(或@Spy)注解创建的对象设置到下面对象中RectangleService rectangleService;//创建bean(类似new RectangleService)@MockRectangleDao rectangleDao;@Beforepublic void setUp() {MockitoAnnotations.initMocks(this);//初始化上面@Mock和@InjectMocks标注对象}@Testpublic void testRectangleService() throws Exception{//打桩PowerMockito.when(rectangleDao.getRectangleById("1")).thenReturn(new MyRectangle(2,3));//构造期望数据,计算实际数据,比对两者MyRectangle myRectangle = new MyRectangle(2,3);int expectedArea = myRectangle.getArea();//调用实际数据并对比int actualArea = rectangleService.getRectangleAreaById("1");Assert.assertEquals(expectedArea, actualArea);}}
方式2:使用spring-boot-test框架对代码进行测试
代码示例:
使用springboot-test的注解。
@RunWith(SpringRunner.class)
@SpringBootTest
public class RectangleServiceTest2 {@Autowired //注入spring IOC容器管理的beanRectangleService rectangleService;//创建bean(类似new RectangleService)@MockBean //mock对象RectangleDao rectangleDao;@Testpublic void testRectangleService() throws Exception{//打桩Mockito.when(rectangleDao.getRectangleById("1")).thenReturn(new MyRectangle(2,3));//构造期望数据,计算实际数据,比对两者MyRectangle myRectangle = new MyRectangle(2,3);int expectedArea = myRectangle.getArea();//调用实际数据并对比int actualArea = rectangleService.getRectangleAreaById("1");Assert.assertEquals(expectedArea, actualArea);}}
三、PowerMock框架核心方法
3.1 PowerMock创建mock对象
T PowerMock.mock(Class<T> type);//创建模拟对象,支持final和native方法
public static <T> void spy(Class<T> type);//创建真实对象
代码示例:
//方式1:注解
@Mock
RectangleDao rectangleDao;
//方式2:创建
RectangleDao rectangleDao = PowerMockito.mock(RectangleDao.class);//方式1:注解
@Spy
RectangleDao rectangleDao;
//方式2:创建
RectangleDao rectangleDao = PowerMockito.spy(new RectangleDao);
3.2 对mock对象方法进行打桩
- 当使用PowerMockito.whenNew方法时,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是需要mock的new对象代码所在的类。
- 当需要mock final方法的时候,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是final方法所在的类。
- 当需要mock静态方法的时候,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是静态方法所在的类。
- 当需要mock私有方法的时候, 只是需要加注解@PrepareForTest,注解里写的类是私有方法所在的类
- 当需要mock系统类的静态方法的时候,必须加注解@PrepareForTest和@RunWith。注解里写的类是需要调用系统方法所在的类
(1)mock非final类(接口、普通类、虚基类)的非final方法:
先mock对象,然后对mock的对象进行打桩顶起方法行为,最后比对结果。
public class RectangleTest {@Testpublic void testObjectNormalMethod(){MyRectangle myRectangle = PowerMockito.mock(MyRectangle.class);PowerMockito.when(myRectangle.getArea()).thenReturn(6);int expectArea = 6;int actualArea = myRectangle.getArea();Assert.assertEquals(expectArea,actualArea);}
}
注:其中when(...).thenReturn(...)表示,当对象 rectangle
调用 getArea(0)
方法,并且参数为 0
时,返回结果为0
,这相当于定制了mock
对象的行为结果.
(2)模拟final类或final方法:
final方法所在的类,需要通过@PrepareForTest注解设置,然后mock对象,再然后打桩(指定方法行为),最后对比。
@Test@PrepareForTest(MyRectangle.class)public void testObjectFinalMethod(){MyRectangle myRectangle = PowerMockito.mock(MyRectangle.class);PowerMockito.when(myRectangle.getFinalArea()).thenReturn(6);int expectArea = 6;int actualArea = myRectangle.getFinalArea();Assert.assertEquals(expectArea,actualArea);}
(3)mockStatic方法
static方法所在的类,需要通过@PrepareForTest注解设置,mock静态方法所在的类,再然后打桩(指定方法行为),最后对比。
public static void mockStatic(Class<?> classToMock);//模拟类的静态方法
@Test@PrepareForTest(MyRectangle.class)public void testObjectStaticMethod(){PowerMockito.mockStatic(AreaUtils.class);PowerMockito.when(AreaUtils.getStaticArea(new MyRectangle(2,3))).thenReturn(6);int expectArea = 6;int actualArea = AreaUtils.getStaticArea(new MyRectangle(2,3));Assert.assertEquals(expectArea,actualArea);}
(4)mock方法的参数
mock不好实际调用的类,然后打桩mock的参数对象的方法行为,最后对比结果。
AreaUtils类:
public class AreaUtils {public static int getStaticArea(MyRectangle rectangle){return rectangle.height * rectangle.width;}public boolean callArgumentInstance(File file) {return file.exists();}
}
测试代码:
@Testpublic void testMockObject() {File file = PowerMockito.mock(File.class);//假设file对象不好实现,这里构造一个file对象AreaUtils areaUtils = new AreaUtils();PowerMockito.when(file.exists()).thenReturn(true);//定制file对象的方法行为:file.exists方法时,设置其返回值为trueAssert.assertTrue(areaUtils.callArgumentInstance(file));//传入构造好的file对象。由于造的file对象的file.exists()方法返回值为true,因此调用demo.call方法返回的就是true}
打桩方法总结:
when().thenReturn()
when().thenThrow()
when().thenCallRealMethod()
when(file.exists()).thenThrow(Exception.class);
whenNew(File.class).withArguments("bbb").thenReturn(file);
PowerMock测试框架详解相关推荐
- Spring MVC测试框架详解——服务端测试
随着RESTful Web Service的流行,测试对外的Service是否满足期望也变的必要的.从Spring 3.2开始Spring了Spring Web测试框架,如果版本低于3.2,请使用sp ...
- 【07节】Python3+Selenium4自动化 unittest 测试框架详解
文章目录 1.unittest 框架介绍 2.创建单元测试步骤 3.unittest 模块介绍 3.1 TestCase 类 3.1.1 TestCase 类常用方法: 3.1.2 TestCase ...
- Python pytest测试框架详解
pytest介绍: pytest是一个非常成熟的全功能的Python测试框架: 1.简单灵活,容易上手 2.支持参数化 3.测试用例的skip和xfail,自动失败重试等处理 4.能够支持简单的单元测 ...
- websocket接口自动化集成pytest测试框架详解
目录 websocket协议 1.介绍 2.原理 3.与http协议的比较 4.websocket应用场景 自动化测试资源分享 websocket协议 1.介绍 WebSocket是一种在单个TCP通 ...
- shiro放行_Shiro框架详解 tagline
部分面试资料链接:https://pan.baidu.com/s/1qDb2YoCopCHoQXH15jiLhA 密码:jsam 想获得全部面试必看资料,关注公众号,大家可以在公众号后台回复" ...
- WebDriver自动化测试框架详解
webDriver自动化测试框架详解 一. 简介 WebDriver API相对于Selenium Remote Control API来说,虽然同样是控制浏览器,但它的编程接口更加简洁 WebDri ...
- springboot2整合mysql5_SpringBoot2整合SSM框架详解
SpringBoot2整合SSM框架详解 发布时间:2019-01-15 21:33, 浏览次数:1218 , 标签: SpringBoot SSM <>开发环境 * 开发工具:Eclip ...
- (转) shiro权限框架详解06-shiro与web项目整合(上)
http://blog.csdn.net/facekbook/article/details/54947730 shiro和web项目整合,实现类似真实项目的应用 本文中使用的项目架构是springM ...
- 集合框架详解之Set、Map接口与实现类
集合框架 1.Set集合 1.1Set集合概述 Set实现类:HashSet类.TreeSet类 特点:无序.无下标.元素不可重复 方法:全部继承自Collection中的方法 1.11 Set接口的 ...
最新文章
- 【100】新学年的学习安排
- 从S3获取数据在html表示,AWS Lambda从DynamoDB加载内容,以S3格式显示在HTML中(示例代码)...
- [Spring]IoC容器之进击的注解
- 笔记-项目沟通管理-沟通基本原则
- vue html2canvas用法,vue中html2canvas的使用(地图截图)
- 软件登录界面设计分享
- Apache Shiro java安全框架
- CSS3的线性渐变(linear-gradient)
- java web获取请求体内容
- cloudflare免费设置_Cloudflare 入门教程:使用 Cloudflare 免费 CDN 加速 amp; 保护自己的网站...
- 斐讯k2路由器刷PandoraBox一宽带多人用
- 空时编码的理论与实践(3)空时编码设计准则
- ZZULIOJ 1190: 按出生日期排序(结构体专题)
- 务实至上:“PHP之父”Rasmus Lerdorf访谈录
- ADS 2019 安装 仿真
- 计算机应用办公自动化技术,计算机应用技术有哪些课程【计算机信息传输技术在办公自动化的应用】...
- 中科断网神器的功能及界面
- 注册表修改3389端口号
- 2012最新超全个性签名,走过路过不要错过哦!
- 模糊PID(重心法解模糊梯形图FC)
热门文章
- 微信小程序[ app.json 文件内容错误] app.json: app.json 未找到解决方法
- Python判断字符串相等
- python 判断list 中重复元素最后1次出现的位置
- 鸿蒙系统 p40 尺寸,P40尺寸
- < 每日知识点:关于Javascript 精进小妙招 ( Js技巧 ) >
- WindTerm 开源的高性能终端模拟器 最酷
- 对于“优盘变成了RAW的文件系统,双击提示需要格式化,右键查看属性是0字节,也无法打开”的解决办法
- 用github教育优惠申请jetbrains学生认证时遇到的问题
- 腾讯云机器学习平台TI-ONE
- 蓝桥杯 基础练习 闰年判断