zoukankan      html  css  js  c++  java
  • 单元测试——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 }
  • 相关阅读:
    Azure 云助手主要功能
    静态dll的问题终于搞定了
    青云QingCloud黄允松:最高效的研发管理就是没有管理
    开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石
    青云QingCloud宣布完成C轮融资,金额1亿美元
    NET MVC权限验证
    Fizz-Buzz-Whizz
    API访问客户端
    使用IronPython给.Net程序
    Smart Framework
  • 原文地址:https://www.cnblogs.com/gzhcsu/p/13381161.html
Copyright © 2011-2022 走看看