单元测试——PowerMock总结
最近项目中单元测试覆盖率要求越来越高,之前Junit和Mock已经无法胜任,所以引入了更强大的PowerMock,方便我们解决静态方法,私有方法等。因此,本文输出PowerMock作为实际使用的小结。
maven项目引入依赖(版本号看实际需要)
1 <dependency> 2 <groupId>org.powermock</groupId> 3 <artifactId>powermock-module-junit4</artifactId> 4 <version>${powermock.version}</version> 5 <scope>test</scope> 6 </dependency> 7 <dependency> 8 <groupId>org.powermock</groupId> 9 <artifactId>powermock-api-mockito</artifactId> 10 <version>${powermock.version}</version> 11 <scope>test</scope> 12 </dependency>
普通POJO
public class User { private String name; private String password; }
普通Dao
1 public class UserDao { 2 3 public int getCount(){ 4 throw new UnsupportedOperationException(); 5 } 6 7 public void saveUser(User user){ 8 throw new UnsupportedOperationException(); 9 } 10 11 /** 12 * 静态方法 13 */ 14 public static int removeUser(User user){ 15 throw new UnsupportedOperationException(); 16 } 17 18 }
普通Service
1 public class UserService { 2 3 private UserDao userDao; 4 5 public UserService(UserDao userDao) { 6 this.userDao = userDao; 7 } 8 9 public int getUserCount() { 10 return userDao.getCount(); 11 } 12 13 public void saveUser(User user) { 14 userDao.saveUser(user); 15 } 16 17 /** 18 * 调用了静态方法 19 */ 20 public int removeUser(User user) { 21 return UserDao.removeUser(user); 22 } 23 24 }
一、针对普通方法的测试
(1)有参返回——Mock和PowerMock都能处理
1 public class UserServiceTest { 2 3 @Test 4 public void getUserCountwithMockito() { 5 UserDao userDao = Mockito.mock(UserDao.class); 6 Mockito.when(userDao.getCount()).thenReturn(10); 7 UserService userService = new UserService(userDao); 8 int resutl = userService.getUserCount(); 9 Assert.assertEquals(10, resutl); 10 Mockito.verify(userDao, Mockito.times(1)).getCount(); 11 } 12 13 @Test 14 public void getUserCountwithPowerMockito() { 15 UserDao userDao = PowerMockito.mock(UserDao.class); 16 PowerMockito.when(userDao.getCount()).thenReturn(5); 17 UserService userService = new UserService(userDao); 18 int resutl = userService.getUserCount(); 19 Assert.assertEquals(5, resutl); 20 } 21 22 }
(2)无参返回——Mock和PowerMock都能处理
1 public class UserServiceTest { 2 3 @Test 4 public void saveUserwithMock() { 5 // User user = Mockito.mock(User.class); 6 User user = new User(); 7 UserDao userDao = Mockito.mock(UserDao.class); 8 Mockito.doNothing().when(userDao).saveUser(user); 9 UserService userSerivce = new UserService(userDao); 10 userSerivce.saveUser(user); 11 Mockito.verify(userDao, Mockito.times(1)).saveUser(user); 12 } 13 14 15 @Test 16 public void saveUserwithPowerMock() { 17 // User user = new User(); 18 User user = PowerMockito.mock(User.class); 19 UserDao userDao = PowerMockito.mock(UserDao.class); 20 PowerMockito.doNothing().when(userDao).saveUser(user); 21 UserService userService = new UserService(userDao); 22 userService.saveUser(user); 23 24 } 25 26 }
二、针对静态方法——只能用PowerMock处理了
当调用UserDao的静态方法时,通过@PrepareForTest注解提前准备好一个UserDao类。
1 @RunWith(PowerMockRunner.class) 2 @PrepareForTest({UserDao.class}) 3 public class UserServiceTest { 4 /** 5 * 处理静态方法-PowerMock,在类上添加两个注解: 7 * @RunWith(PowerMockRunner.class) 8 * @PrepareForTest(UserDao.class) 9 */ 10 @Test 11 public void removeUserwithPowerMock() { 12 User user = PowerMockito.mock(User.class); 13 PowerMockito.mockStatic(UserDao.class); 14 PowerMockito.when(UserDao.removeUser(user)).thenReturn(1); 15 UserService userService = new UserService(new UserDao()); 16 int result = userService.removeUser(user); 17 Assert.assertEquals(1, result); 18 } 19 }
三、局部变量——只能用PowerMock处理了
普通POJO
import lombok.Getter; import lombok.Setter; @Getter @Setter public class Employee { private String name; private String password; }
普通Dao
1 public class EmployeeDao { 2 3 public int getTotalCount(Employee employee) { 4 throw new UnsupportedOperationException(); 5 } 6 7 public void creatEmployee(Employee employee) { 8 throw new UnsupportedOperationException(); 9 } 10 11 public void updateEmployee(Employee employee) { 12 throw new UnsupportedOperationException(); 13 } 14 15 public int getEmployeeCount(Employee employee) { 16 throw new UnsupportedOperationException(); 17 } 18 19 public String getEmployeeByName(String name){ 20 throw new UnsupportedOperationException(); 21 } 22 }
普通Service
1 public class EmployeeService { 2 3 /** 4 * 局部变量:内部无参构造创建对象,整个方法有返回值 5 */ 6 public int getTotalCount(Employee employee) { 7 EmployeeDao employeeDao = new EmployeeDao(); 8 return employeeDao.getTotalCount(employee); 9 } 10 /** 11 * 局部变量:内部无参构造创建对象,整个方法无返回值 12 */ 13 public void createEmployee(Employee employee) { 14 EmployeeDao employeeDao = new EmployeeDao(); 15 employeeDao.creatEmployee(employee); 16 } 17 18 /** 19 * 方法内有分支 20 */ 21 public void saveOrUpdate(Employee employee){ 22 EmployeeDao employeeDao = new EmployeeDao(); 23 int count = employeeDao.getEmployeeCount(employee); 24 if(count>0){ 25 employeeDao.updateEmployee(employee); 26 }else{ 27 employeeDao.creatEmployee(employee); 28 } 29 } 30 }
3.1 测试“有返回值”
@PrepareForTest(EmployeeService.class):让创建出来的EmployeeService实例,采用事先准备好的(@PrepareForTest帮助实现,底层通过改变字节码)。
PowerMockito.whenNew(EmployeeDao.class).withNoArguments().thenReturn(employeeDao):利用PowerMockito创建无参构造类的实例(通过mock已经准备,如PowerMockito.mock(Employee.class)
),其中 withNoArguments()会抛异常,需要try...catch处理。
构造函数说明
(1)PowerMockito.whenNew(EmployeeDao.class).withNoArguments()
(2)PowerMockito.whenNew(EmployeeDao.class).withAnyArguments()
(3)PowerMockito.whenNew(EmployeeDao.class).withArguments(Object firstArgument, Object... additionalArguments)
1 @RunWith(PowerMockRunner.class) 2 @PrepareForTest(EmployeeService.class) 3 public class EmployeeServiceTest { 4 5 /** 6 * 测试有返回值,在类上添加两个注解: 7 * 8 * @RunWith(PowerMockRunner.class) 9 * @PrepareForTest(EmployeeService.class) 10 */ 11 @Test 12 public void getTotalCountTest() { 13 Employee employee = PowerMockito.mock(Employee.class); 14 EmployeeDao employeeDao = PowerMockito.mock(EmployeeDao.class); 15 try { 16 PowerMockito.whenNew(EmployeeDao.class).withNoArguments().thenReturn(employeeDao); 17 PowerMockito.when(employeeDao.getTotalCount(employee)).thenReturn(5); 18 EmployeeService employeeService = new EmployeeService(); 19 int totalCount = employeeService.getTotalCount(employee); 20 Assert.assertEquals(5, totalCount); 21 } catch (Exception e) { 22 fail("测试失败..."); 23 } 24 } 25 }
3.2 测试无返回值
PowerMockito.doNothing().when(employeeDao).creatEmployee(employee);
1 @RunWith(PowerMockRunner.class) 2 @PrepareForTest(EmployeeService.class) 3 public class EmployeeServiceTest { 4 /** 5 * 测试无返回值,在类上添加两个注解: 6 * 7 * @RunWith(PowerMockRunner.class) 8 * @PrepareForTest(EmployeeService.class) 9 */ 10 @Test 11 public void creatEmplyeeTest() { 12 Employee employee = PowerMockito.mock(Employee.class); 13 14 EmployeeDao employeeDao = PowerMockito.mock(EmployeeDao.class); 15 try { 16 PowerMockito.whenNew(EmployeeDao.class).withNoArguments().thenReturn(employeeDao); 17 PowerMockito.doNothing().when(employeeDao).creatEmployee(employee); 18 EmployeeService employeeService = new EmployeeService(); 19 employeeService.createEmployee(employee); 20 Mockito.verify(employeeDao, Mockito.times(1)).creatEmployee(employee); 21 } catch (Exception e) { 22 fail("测试失败..."); 23 } 24 } 25 }
3.3 通过Mockito.verify验证分支
通过PowerMockito.when(employeeDao.getEmployeeCount(employee)).thenReturn(0):中thenReturn的返回值可以走向不同的分支。
1 @RunWith(PowerMockRunner.class) 2 @PrepareForTest(EmployeeService.class) 3 public class EmployeeServiceTest { 4 /** 5 * 测试分支及Mockito.verify,在类上添加两个注解: 6 * 7 * @RunWith(PowerMockRunner.class) 8 * @PrepareForTest(EmployeeService.class) 9 */ 10 @Test 11 public void saveOrUpdateTest1() { 12 Employee employee = PowerMockito.mock(Employee.class); 13 EmployeeDao employeeDao = PowerMockito.mock(EmployeeDao.class); 14 try { 15 16 PowerMockito.whenNew(EmployeeDao.class).withNoArguments().thenReturn(employeeDao); 17 PowerMockito.when(employeeDao.getEmployeeCount(employee)).thenReturn(0); 18 19 EmployeeService employeeService = new EmployeeService(); 20 employeeService.saveOrUpdate(employee); 21 22 Mockito.verify(employeeDao).creatEmployee(employee); 23 Mockito.verify(employeeDao, Mockito.never()).updateEmployee(employee); 24 } catch (Exception e) { 25 fail("测试失败..."); 26 } 27 } 28 29 /** 30 * 测试分支及Mockito.verify,在类上添加两个注解: 31 * 32 * @RunWith(PowerMockRunner.class) 33 * @PrepareForTest(EmployeeService.class) 34 */ 35 @Test 36 public void saveOrUpdateTest2() { 37 Employee employee = PowerMockito.mock(Employee.class); 38 EmployeeDao employeeDao = PowerMockito.mock(EmployeeDao.class); 39 try { 40 41 PowerMockito.whenNew(EmployeeDao.class).withNoArguments().thenReturn(employeeDao); 42 PowerMockito.when(employeeDao.getEmployeeCount(employee)).thenReturn(1); 43 44 EmployeeService employeeService = new EmployeeService(); 45 employeeService.saveOrUpdate(employee); 46 47 Mockito.verify(employeeDao, Mockito.never()).creatEmployee(employee); 48 Mockito.verify(employeeDao).updateEmployee(employee); 49 } catch (Exception e) { 50 fail("测试失败..."); 51 } 52 } 53 }
四、参数使用
4.1 org.mockito.Matchers使用
1 /** 2 * org.mockito.Matchers使用:Matchers.anyString() 3 * 4 * @RunWith(PowerMockRunner.class) 5 * @PrepareForTest(EmployeeService.class) 6 */ 7 @Test 8 public void getEmployeeByNameTest1() throws Exception { 9 EmployeeDao employeeDao = PowerMockito.mock(EmployeeDao.class); 10 PowerMockito.whenNew(EmployeeDao.class).withNoArguments().thenReturn(employeeDao); 11 PowerMockito.when(employeeDao.getEmployeeByName(Matchers.anyString())).thenReturn("hello"); 12 EmployeeService employeeService = new EmployeeService(); 13 employeeService.getEmployeeByName(Matchers.anyString()); 14 }
4.2 ArgumentMatcher使用
注意还使用了Matchers.argThat(new MyArgumentMatcher())
/** * ArgumentMatcher使用 * * @RunWith(PowerMockRunner.class) * @PrepareForTest(EmployeeService.class) */ @Test public void getEmployeeByNameTest2() throws Exception { EmployeeDao employeeDao = PowerMockito.mock(EmployeeDao.class); PowerMockito.whenNew(EmployeeDao.class).withNoArguments().thenReturn(employeeDao); PowerMockito.when(employeeDao.getEmployeeByName(Matchers.argThat(new MyArgumentMatcher()))).thenReturn("hello"); EmployeeService service = new EmployeeService(); Assert.assertEquals("hello", service.getEmployeeByName("selectOne")); Assert.assertEquals("hello", service.getEmployeeByName("selectTwo")); Assert.assertEquals("hello", service.getEmployeeByName("selectThree")); /** * 如果采用其他String就会报错,如下: * Assert.assertEquals("hello",service.getEmployeeByName("Others")); */ } private class MyArgumentMatcher extends ArgumentMatcher<String> { @Override public boolean matches(Object argument) { String value = (String) argument; switch (value) { case "selectOne": case "selectTwo": case "selectThree": return true; default: return false; } } }
五、Answer接口使用
public interface Answer<T> { /** * @param invocation the invocation on the mock. * * @return the value to be returned * * @throws Throwable the throwable to be thrown */ T answer(InvocationOnMock invocation) throws Throwable; }
1 @Test 2 public void getEmployeeByNameTest3() throws Exception { 3 EmployeeDao employeeDao = PowerMockito.mock(EmployeeDao.class); 4 PowerMockito.whenNew(EmployeeDao.class).withNoArguments().thenReturn(employeeDao); 5 /** 6 * Example of stubbing a mock with custom answer: 7 * 8 * when(mock.someMethod(anyString())).thenAnswer(new Answer() { 9 * Object answer(InvocationOnMock invocation) { 10 * Object[] args = invocation.getArguments(); 11 * Object mock = invocation.getMock(); 12 * return "called with arguments: " + Arrays.toString(args); 13 * } 14 * }); 15 */ 16 PowerMockito.when(employeeDao.getEmployeeByName(Mockito.anyString())).then(invocation ->{ 17 Object[] args = invocation.getArguments(); 18 System.out.println(); 19 System.out.println(invocation.getMethod()); 20 System.out.println(Arrays.asList(args)); 21 String value = (String)args[0]; 22 switch (value){ 23 case "one":return "hello one"; 24 case "two":return "hello two"; 25 case "three":return "hello three"; 26 default: 27 return new RuntimeException("Not support: " + value ); 28 } 29 }); 30 EmployeeService service = new EmployeeService(); 31 Assert.assertEquals("hello one", service.getEmployeeByName("one")); 32 Assert.assertEquals("hello two", service.getEmployeeByName("two")); 33 Assert.assertEquals("hello three", service.getEmployeeByName("three")); 34 try { 35 String others = service.getEmployeeByName("others"); 36 }catch (Exception e){ 37 Assert.assertTrue(e instanceof RuntimeException); 38 } 39 }
六、Spy使用和私有方法mock
普通Service
1 public class MyService { 2 public void foo(String arg){ 3 this.printLog(arg); 4 } 5 private void printLog(String arg){ 6 System.out.println("hello " + arg); 7 } 8 9 public boolean exist(String args){ 10 return this.checkExist(args); 11 } 12 /** 13 * 该方法做很多业务逻辑 14 */ 15 private boolean checkExist(String args){ 16 throw new UnsupportedOperationException(); 17 } 18 }
(1)mock与spy区别:mock不去内部执行,而spy会;spy也可以不执行,如:PowerMockito.doNothing().when(service).foo(arg)
(2)私有方法也可以mock:PowerMockito.doReturn(true).when(service, "checkExist", "world");
源码如下:
1 /** 2 * Allows to mock a private instance method based on method name and 3 * parameters when stubbing in doThrow()|doAnswer()|doNothing()|doReturn() 4 * style. 5 * <p> 6 * Example: 7 * 8 * <pre> 9 * doThrow(new RuntimeException()).when(instance, "methodName", parameter1, parameter2); 10 * </pre> 11 * 12 * Read more about those methods: 13 * <p> 14 * {@link Mockito#doThrow(Throwable)} 15 * <p> 16 * {@link Mockito#doAnswer(Answer)} 17 * <p> 18 * {@link Mockito#doNothing()} 19 * <p> 20 * {@link Mockito#doReturn(Object)} 21 * <p> 22 * 23 * See examples in javadoc for {@link Mockito} 24 */ 25 <T> void when(T mock, String methodToExpect, Object... arguments) throws Exception;
测试代码如下:
1 @RunWith(PowerMockRunner.class) 2 @PrepareForTest({MyService.class}) 3 public class MyServiceTest { 4 5 /** 6 * ① 需要注解 7 * @RunWith(PowerMockRunner.class) 8 * @PrepareForTest({MyService.class}) 9 * 10 * ② 私有方法checkExist可以mock 11 * 12 * ③参数必须同一个:都是world才能通过 13 */ 14 @Test 15 public void fooTest5() throws Exception { 16 MyService service = PowerMockito.spy(new MyService()); 17 PowerMockito.doReturn(true).when(service, "checkExist", "world"); 18 Assert.assertTrue(service.exist("world")); 19 } 20 21 /** 22 * 控制台打印输出: 23 * hello other words 24 */ 25 @Test 26 public void fooTest4() { 27 MyService service = PowerMockito.spy(new MyService()); 28 String arg = "world"; 29 PowerMockito.doNothing().when(service).foo(arg); 30 service.foo("other words"); 31 } 32 33 /** 34 * 控制台无打印:doNothing() 35 */ 36 @Test 37 public void fooTest3() { 38 MyService service = PowerMockito.spy(new MyService()); 39 String arg = "world"; 40 PowerMockito.doNothing().when(service).foo(arg); 41 service.foo(arg); 42 } 43 44 /** 45 * 控制台打印输出:spy(侦察) 46 * hello world 47 */ 48 @Test 49 public void fooTest2() { 50 MyService service = PowerMockito.spy(new MyService()); 51 service.foo("world"); 52 } 53 54 /** 55 * 控制台无打印:mock其实不去执行内部方法 56 */ 57 @Test 58 public void fooTest1() { 59 MyService service = PowerMockito.mock(MyService.class); 60 service.foo("world"); 61 } 62 }
posted on 2020-07-26 17:42 betterLearing 阅读(1675) 评论(0) 编辑 收藏 举报