1. 程式人生 > 實用技巧 >單元測試——PowerMock總結

單元測試——PowerMock總結

最近專案中單元測試覆蓋率要求越來越高,之前Junit和Mock已經無法勝任,所以引入了更強大的PowerMock,方便我們解決靜態方法,私有方法等。因此,本文輸出PowerMock作為實際使用的小結。

普通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

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 }

普通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 }