单元测试——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, &quot;methodName&quot;, 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编辑  收藏  举报