PowerMock(一):PowerMock的基本使用
文章目錄
為啥要使用PowerMock
PowerMock的使用
環(huán)境
引入依賴
注解說明
mock普通方法
mock拋出異常
mock新建對象
mock無返回值的方法
mock被final修飾的方法
參數(shù)模糊匹配
mock靜態(tài)方法
mock私有方法
總結
參考
為啥要使用PowerMock
現(xiàn)在流行的測試驅(qū)動開發(fā)TDD(Test-Driven Development) ,是敏捷開發(fā)中一項核心實踐和技術。也是一種設計方法論。其中最重要的一環(huán)就是使用單元測試。
單元測試是保證代碼質(zhì)量的一個重要手段,通過單元測試我們可以快速的測試代碼的各個分支,各種場景,代碼重構時只需要重新跑下單元測試就是能知道代碼潛在的問題。
單元測試是通過Mock的方式調(diào)用被測試的方法,其有如下幾個優(yōu)點:
Mock可以解除測試對象對外部服務的依賴(比如數(shù)據(jù)庫,第三方接口等),使得測試用例可以獨立運行。不管是單體應用還是微服務,這點都特別重要。
Mock的第二個好處就是替換外部服務調(diào)用,提升測試用例的運行速度。因為任何外部服務調(diào)用至少是跨進程級別的消耗,甚至是跨系統(tǒng)、跨網(wǎng)絡的消耗,而Mock可以把消耗降低到進程內(nèi)。
Mock的第三個好處就是提升測試效率,提高單位時間內(nèi)測試的接口數(shù)量。
Mock的框架有很多中比如EasyMock等,這里選用PowerMock是因為PowerMock可以用來Mock 私有方法,靜態(tài)方法以及final方法。EasyMock等則不能。
PowerMock的使用
環(huán)境
軟件 版本
junit 4.13
powermock 2.0.7
引入依賴
<properties>
<powermock.version>2.0.7</powermock.version>
</properties>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<!--powermock開始-->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<!--powermock結束-->
這里引入了是三個依賴,junit依賴如果項目中已有的話,則不需要重復引入,需要注意的是JUnit 4.4及以上版本的JUnit需要引入2.0.x 版本以上的 powermock 。如果項目中有mockito依賴還需要注意mockito的版本與powermock版本對應關系,對應如下圖:
詳細請參考Using PowerMock with Mockito,如果引入的版本不匹配則可能會報如下錯誤:
java.lang.TypeNotPresentException: Type org.powermock.modules.junit4.PowerMockRunner not present
依賴引入之后就可以編寫單元測試代碼了。
注解說明
現(xiàn)有一個待測試的類UserServiceImpl,該類中注入了一個UserMapper的類實例。
@Service
public class UserServiceImpl {
@Autowried
private UserMapper userMapper;
........省略部分方法
}
那么如何對上面的類通過powermock的方式進行單元測試呢?首先是定義一個測試類,定義如下:
@RunWith(PowerMockRunner.class)
@PrepareForTest({UserServiceImpl.class, DateUtil.class, UserMapper.class})
public class UserServiceImplTest {
@Mock
private UserMapper userMapper;
@InjectMocks
private UserServiceImpl userServiceImpl = new UserServiceImpl();
}
@RunWith(PowerMockRunner.class) 注解表明使用PowerMockRunner運行測試用例,這個必須添加,不然無法使用PowerMock。
@PrepareForTest({UserServiceImpl.class, DateUtil.class, UserMapper.class}) @PrepareForTest 注解是用來添加所有需要測試的類,這里列舉了三個需要測試的類。
@Mock注解修飾會mock出來一個對象,這里mock出來的是UserMapper類實例。
@InjectMocks 注解會主動將已存在的mock對象注入到bean中,按名稱注入,這個注解修飾在我們需要測試的類上。必須要手動new一個實例,不然單元測試會有問題。
這幾個注解是一個測試類必須要的。說完了測試類的定義,接下來就讓我們來看看各種方法是如何mock的。
mock普通方法
待測試的方法(UserMapper中)
boolean saveUser(User user) {
int i = userMapper.addUser(user);
return i == 1 ? true : false;
}
這里的方法int i = userMapper.addUser(user); 有入?yún)?,有出參,沒有關鍵字修飾,是一個普通的方法,mock的方式也很簡單,就是PowerMockito.when(userMapper.addUser(user)).thenReturn(1); 在when方法中調(diào)用你需要mock的方法,thenReturn方法寫入你期待返回的值。從字面意思理解就是當調(diào)用xxx方法時,返回xxx值。 詳細的示例如下:
2. 測試方法
User user = new User();
user.setId(1);
user.setUserName("test");
user.setPassword("admin123");
PowerMockito.when(userMapper.addUser(user)).thenReturn(1);
boolean result = userServiceImpl.saveUser(user);
Assert.assertEquals(true, result);
mock拋出異常
單元測試中我們有時候需要mock異常的拋出,其mock的方式也很簡單就是在thenThrow(new Exception())寫入你期待拋出的異常。如果被mock的方法拋出的是受檢異常(checked exception)的話,那么thenThrow拋出new Exception()或者其子類。
如果被mock的方法拋出的是非受檢異常(unchecked exception),那么thenThrow拋出new RuntimeException或其子類。使用的示范如下:
待測試的方法(UserMapper中)
int delUser(int id) throws Exception {
if (id == -1) {
throw new Exception("傳入的id值不對");
} else {
return 1;
}
}
測試方法
PowerMockito.when(userMapper.delUser(-1)).thenThrow(new Exception());
這里delUser方法拋出的是受檢異常Exception,所以在thenThrow中需要new一個Exception對象。
mock新建對象
如果我們要對一個實體對象Bean進行Mock,只需要這樣寫PowerMockito.whenNew(User.class).withAnyArguments().thenReturn(user)
這個代碼的意思是創(chuàng)建一個User實例對象,不管傳入啥參數(shù)都返回定義的實例user,用于替換被測試方法中相應的User對象。使用示范如下:
待測試的方法(UserMapper中)
public int countUser() {
User user = new User();
int count = 0;
if (user.getId() > 0) {
count += 1;
}
return count;
}
測試方法
// 6.mock新建對象
User user = new User();
user.setId(11);
PowerMockito.whenNew(User.class).withAnyArguments().thenReturn(user);
int result = userServiceImpl.countUser();
Assert.assertEquals(1, result);
這里mock了一個User對象。id是11,當調(diào)用countUser方法時可以拿到之前mock的User對象,所以返回的結果是1。
mock無返回值的方法
對于返回值是通過void修飾的方法,他的mock方式與普通方法的mock方式不同。有兩種方式mock。
方式一:
PowerMockito.doNothing().when(userMapper, "updateUser", new User());
在when方法中傳入userMapper類實例,需要調(diào)用的方法名,以及需要傳入的參數(shù)。
方式二:
PowerMockito.doNothing().when(userMapper).updateUser(user);
在when方法中只傳入userMapper類實例,然后通過函數(shù)式調(diào)用的方式調(diào)用待測試的方法。
使用示范如下:
待測試的方法(UserServiceImpl中)
public void updateUser(User user) {
userMapper.updateUser(user);
}
測試方法
User user = new User();
// 4.mock返回值為void的方法
//方法一
PowerMockito.doNothing().when(userMapper, "updateUser", new User());
//方法二
PowerMockito.doNothing().when(userMapper).updateUser(user);
userServiceImpl.updateUser(user);
mock被final修飾的方法
現(xiàn)在有一個方法被final關鍵字修飾,那么該如何要mock這個方法,首先需要mock出一個類實例。如下所示:
UserMapper mock = PowerMockito.mock(UserMapper.class);
這里需要特別注意的是被mock的類必須要在@PrepareForTest注解中指定,如本例中的@PrepareForTest({UserMapper.class})。不然就會報如下錯誤:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
使用示范如下:
待測試的方法(UserMapper中)
final String getUserName() {
return "admin";
}
測試方法
UserMapper mock = PowerMockito.mock(UserMapper.class);
when(mock.getUserName()).thenReturn("123");
參數(shù)模糊匹配
前面的測試方法中,參數(shù)我們都是指定的,在一些場景下,對于一些比較復雜的參數(shù),我們不好構造,這時候參數(shù)模糊匹配就派上用場了。如下所示,現(xiàn)有方法selectUser,他有三個參數(shù),參數(shù)類型個不相同。
User selectUser(Integer id, String userName, String password)
當對這個方法進行mock時,可以不用傳入具體的參數(shù)值。就行這樣進行mock。
PowerMockito.when(userMapper.selectUser(ArgumentMatchers.anyInt(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())).thenReturn(user);
其中ArgumentMatchers.anyInt()是指任意的int類型的值,ArgumentMatchers.anyString()是指任意String類型的值。需要特別注意的是一個方法中只要有一個參數(shù)使用了模糊匹配,其余的參數(shù)也都需要使用模糊匹配。
mock靜態(tài)方法
對靜態(tài)方法的mock也比較簡單,與普通方法的mock相比只是多了一行代碼。就是首先需要對靜態(tài)方法的所在的類進行mock。
PowerMockito.mockStatic(DateUtil.class);
同時被mock的類必須要在@PrepareForTest注解中指定,像本例中的DateUtil類。@PrepareForTest({ DateUtil.class}),其他的與普通方法的mock一樣,再此就不在贅述了。
mock私有方法
當我們需要測試的方法中調(diào)用了一個比較復雜的私有方法時,我們該如何mock呢?針對這種情況PowerMock也可以輕松應對。首先調(diào)用spy方法創(chuàng)建出一個新的UserServiceImpl類實例。然后通過這個實例來mock這個私有方法。如下所示:
UserServiceImpl spy = PowerMockito.spy(userServiceImpl);
PowerMockito.when(spy, "verifyId", 0).thenReturn(true);
使用示范:
待測試的方法
boolean delUser(int id) throws Exception {
int i = userMapper.delUser(id);
return verifyId(i);
}
測試方法
public void testVerifyId() throws Exception {
// 5.mock私有方法
UserServiceImpl spy = PowerMockito.spy(userServiceImpl);
PowerMockito.when(spy, "verifyId", 0).thenReturn(true);
PowerMockito.when(userMapper.delUser(0)).thenReturn(1);
Assert.assertEquals(userServiceImpl.delUser(0), true);
}
總結
本文詳細介紹了PowerMock的常見使用,PowerMock是一個應用比較廣泛的單元測試框架,運用在單元測試中可以很好的提供測試效率。PowerMock可以mock 普通方法,私有方法,靜態(tài)方法,final修飾的方法。
參考
無所不能的PowerMock,mock私有方法,靜態(tài)方法,測試私有方法,final類
power mock 入門介紹及使用示例
作者:碼農(nóng)飛哥
微信公眾號:碼農(nóng)飛哥