• 单元测试系统化讲解之PowerMock


    单元测试系统化讲解之PowerMock

    阅读本篇博客前建议有Mockito基础,参见上一篇文章:单元测试系统化讲解之Mockito

    • 什么是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 })
      
      • 1
      • 2
    • 使用了@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>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    • 开始测试:

      1. 编写被测试的类:

        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);
        	}
        }	
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
      2. 测试类:

        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);
        		}
        	}	
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43

    代码中展示了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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 测试类
    @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();
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    2.2 Mock静态方法

    • 静态Dao层
    public class UserDao{
    	
    	public static int getCount(){
    		throw new UnsupportedOperationException();
    	}
    
    	public static void insertUser(User user){
    		throw new UnsupportedOperationException();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 被测试的Service层
    public class UserService{
    
    	public int queryUserCount(){
    		return UserDao.getCount();
    	}
    
    	public void saveUser(User user){
    		UserDao userDao = new UserDao();
    		UserDao.insertUser(user);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 测试类
    @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();
    
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2.3 final 修饰的类

    • final 类
    final public class UserDao{
    	public int getCount(){
    		throw new UnsupportedOperationException();
    	}
    
    	public void insertUser(User user){
    		throw new UnsupportedOperationException();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 测试类
    @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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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();
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 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);
    		}
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 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表示一次都不会执行
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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();
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • UserService
    public class UserService{
    
    	public void save(String username, String password){
    		UserDao userDao = new UserDao(username,password);
    		userDao.insert();
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 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();
    	}
    
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.6 Arguments Matcher的使用

    • UserDao
    public class UserDao{
    	public String queryByName(String username){
    		throw new UnsupportedOperationException();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • UserService
    public class UserService{
    
    	public String find(String name){
    		UserDao userDao = new UserDao();
    		return userDao.queryByName(name);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 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;
    			}
    		}
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    2.7 Answer 接口的使用

    • UserDao
    public class UserDao{
    	public String queryByName(String username){
    		throw new UnsupportedOperationException();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • UserService
    public class UserService{
    
    	public String find(String name){
    		UserDao userDao = new UserDao();
    		return userDao.queryByName(name);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 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"));
    	}	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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();
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 使用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");
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    写文不易,觉得不错点个赞再走吧~ ^^

  • 相关阅读:
    TRC 链格孢菌毒素和基因毒素丨艾美捷 TRC Alternariol 9-龙胆二糖苷
    C/C++结构体使用总结
    一种基于柔性事务的分布式事务解决方案设计探究
    WS-FLV直播协议分析
    【设计模式】工厂模式总结
    新手选MT4老手选MT5,有道理吗?anzo capital昂首资本这样分析
    Linux磁盘管理
    GeoServer源码运行(数据目录+数据库)
    Flink Yarn Per Job - CliFrontend
    Java-钉钉订阅事件
  • 原文地址:https://blog.csdn.net/qq_37128049/article/details/126277141