当前位置:网站首页>PowerMock for Systematic Explanation of Unit Testing
PowerMock for Systematic Explanation of Unit Testing
2022-08-11 09:04:00 【After dark】
单元测试系统化讲解之PowerMock
- 什么是PowerMock
- Mock局部变量
- Mock静态方法
- Mock final 修饰的方法
- Mock私有方法
- Verify的使用
- Mock不同的构造函数
- Parameters Matcher接口的使用
- Answer接口的使用
- Spy的使用
一、什么是PowerMock
1.1 前述
本次讲解的PowerMockIt is an advanced technical framework for unit testing;所以学习PowerMock中,Bloggers assume that you have met the following conditions:
- Know what unit testing is
- 明白Junit/MockitoSome relevant use or knowledge
- Want to learn some techniques related to unit testing
PowerMock是什么?
- PowerMock是一个扩展了其它如EasyMock等mock框架的、功能更加强大的框架.PowerMock使用一个自定义类加载器和字节码操作来模拟静态方法、构造方法、final类和方法、私有方法、去除静态初始化器等等.
- 通过使用自定义的类加载器,简化采用的IDE或持续集成服务器不需要做任何改变.
- 熟悉PowerMock支持的mock框架的开发人员会发现PowerMock很容易使用,因为对于静态方法和构造器来说,整个的期望API是一样的.
- PowerMock旨在用少量的方法和注解扩展现有的API来实现额外的功能.目前PowerMock支持EasyMock和Mockito.
- PowerMockis based on otherMock框架,做的增强.So it can't be used alone.
PowerMock解决了哪些痛点?
- 现如今比较流行的Mock工具如jMock,EasyMock,Mockito等都有一个共同的缺点:不能mock静态、final、私有方法等.而PowerMock能够完美的弥补以上三个Mock工具的不足.
- 能够mock方法内
new 出来的对象
注意:为什么总说PowerMock尽量少用?
- PowerMockis a very powerful and useful tool. 但是,It should only be used when absolutely necessary,Because it can have a huge impact on test execution time.
- 缺乏API知识,Overly restrictive visibility and direct static method calls.
- If you find your test code base fullPowerMock的用法,It is recommended that you try the above methods to get rid of them.
- If there are too many unnecessary unit testsPowerMockito使用,Then the quality of this unit test is to be considered,In particular, some private methods should not be used as much as possiblePowerMockito,善PowerMockito,Try to maximize the fidelity of unit tests.
1.2 PowerMock快速入门
PowerMock有两个重要的注解,分别是:
@RunWith(PowerMockRunner.class) @PrepareForTest( { YourClassWithEgStaticMethod.class })
使用了
@PrepareForTest
注解,能够使用PowerMock的强大功能(mock静态、final、私有方法等);需要注意的是,It cannot work alone,We have to add both@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>
开始测试:
Write the class to be tested:
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、powermockthree test methods.
二、PowerMock实战演示
2.1 Use local variables inside methods
- 被测试类:
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); // neverIndicates that it will not be executed once
}
}
verify有很多方法,Students can look at the source code or official documents to use and practice;
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会调用真实的方法:
如果使用了whenThe function specifies the request parameters of the corresponding method,Called when the corresponding request parameters are passed inmockThe method does not print anything;
而不满足条件,will continue to execute the real method:
测试私有方法:
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");
}
}
写文不易,觉得不错点个赞再走吧~ ^^
边栏推荐
猜你喜欢
深度学习100例 —— 卷积神经网络(CNN)识别眼睛状态
设置Vagrant创建的虚拟机名称和内存
Typescript基本类型---上篇
Unity3D - modification of the Inspector panel of the custom class
框架外的PHP读取.env文件(php5.6、7.3可用版)
前几天,小灰去贵州了
Alibaba Sentinel - Slot chain解析
深度学习100例 —— 卷积神经网络(CNN)识别验证码
One network cable to transfer files between two computers
golang 字符串操作
随机推荐
VoLTE基础自学系列 | 3GPP规范解读之Rx接口(上集)
【wxGlade学习】wxGlade环境配置
One network cable to transfer files between two computers
Initial use of IDEA
当你领导问你“还有其他的么”
表达式必须具有与对应表达式相同的数据类型
基于 VIVADO 的 AM 调制解调(1)方案设计
企业服务器主机加固现状分析
基于consul的注册发现的微服务架构迁移到servicemesh
零基础创作专业wordpress网站12-设置标签栏图标(favicon)
如何通过开源数据库管理工具 DBeaver 连接 TDengine
ES6:数值的扩展
【系统梳理】当我们在说服务治理的时候,其实我们说的是什么?
《价值》读书与投资
SDUT 2877: angry_birds_again_and_again
wordpress插件开发03-简单的all in one seo 插件开发
深度学习100例 —— 卷积神经网络(CNN)识别验证码
前几天,小灰去贵州了
1.3版本自定义TrainOneStepCell报错
Filesystem Hierarchy Standard