在編寫代碼時,總是有方法返回void,並且在某個測試用例需要模擬void方法。那么我們如何去做呢?讓我們一起在下面的內容中使用Mockito完成這個需求。
Mockito是用於編寫單元測試的最著名的模擬框架之一。
為什么模擬void方法
假設我們有一個方法A,在此方法中,使用了另一個void方法B。現在,當要為該方法編寫測試用例時,我們如何測試B方法被調用?另外,是否將正確的參數傳遞給B方法?在這種情況下,Mockito可以幫助我們解決這個問題。
讓我們舉個例子,我們有一個UserService類。在此類中,我們有一個updateName()方法。
public UserService{
public void updateName(Long id, String name){
userRepository.updateName(id, name);
}
}
現在,我們要為UserService類編寫單元測試並模擬userRepository。但是,在此測試用例中,我們唯一需要驗證的是使用正確的參數集調用了userRepository中的updateName()方法。為此,我們需要模擬updateName()方法,捕獲參數並驗證參數。
這里要注意的最重要的是,我們不能僅僅使用Mockito的when-then機制來模擬void方法。因為,Mockito的when()方法適用於返回值,而方法返回值是void時則不適用。
如何在Mockito中模擬void方法
在Mockito中,我們可以使用不同的方法來調用實例方法或模擬void方法。根據要求使用其中一個選項:
doNothing():完全忽略對void方法的調用,這是默認doAnswer():在調用void方法時執行一些運行時或復雜的操作doThrow():調用模擬的void方法時引發異常doCallRealMethod():不要模擬並調用真實方法
使用doNothing()
如果我們只想完全忽略void方法調用,則可以使用doNothing()。
在測試用例中,對於模擬對象的每種方法,doNothing是默認行為。因此,如果不想驗證參數,則使用doNothing是完全可以的。
將doNothing()用於void方法的Demo:
@Test
public void test001() {
doNothing().when(mockedUserRepository).updateName(anyLong(),anyString());
userService.updateName(1L,"FunTester");
verify(mockedUserRepository, times(1)).updateName(1L,"FunTester");
}
不對空方法使用doNothing():
@Test
public void test002() {
userService.updateName(1L,"FunTester");
verify(mockedUserRepository, times(1)).updateName(1L,"FunTester");
}
使用doNothing()進行參數捕獲的示例
@Test
public void testUpdateNameUsingArgumentCaptor() {
ArgumentCaptor<Long> idCapture = ArgumentCaptor.forClass(Long.class);
ArgumentCaptor<String> nameCapture = ArgumentCaptor.forClass(String.class);
doNothing().when(mockedUserRepository).updateName(idCapture.capture(),nameCapture.capture());
userService.updateName(1L,"FunTester");
assertEquals(1L, idCapture.getValue());
assertEquals("FunTester", nameCapture.getValue());
}
將doAnswer()用於void方法
如果我們不想調用真實方法,則需要執行一些運行時操作,請使用doAnswer()。
下面是使用doAnswer()打印並驗證參數的Demo:
@Test
public void testUpdateNameUsingDoAnswer() {
doAnswer(invocation -> {
long id = invocation.getArgument(0);
String name = invocation.getArgument(1);
System.out.println("called for id: "+id+" and name: "+name);
assertEquals(1L, id);
assertEquals("FunTester", name);
return null;
}).when(mockedUserRepository).updateName(anyLong(),anyString());
userService.updateName(1L,"FunTester");
verify(mockedUserRepository, times(1)).updateName(1L,"FunTester");
}
使用doThrow()引發異常
如果要在調用方法時引發異常,則可以使用嘲笑的doThrow()方法。
讓我們舉一個例子:當使用null作為id調用updateName()方法時,我們將引發InvalidParamException。
@Test(expected = InvalidParamException.class)
public void testUpdateNameThrowExceptionWhenIdNull() {
doThrow(new InvalidParamException())
.when(mockedUserRepository).updateName(null,anyString();
userService.updateName(null,"FunTester");
}
使用doCallRealMethod()進行真實方法調用
有時有必要從模擬對象中調用真實方法,在這種情況下,我們需要使用doCallRealMethod(),因為doNothig()是默認行為。
在以下示例中,即使是模擬對象,也會調用userRepository中的真實方法。
@Test
public void testUpdateNameCallRealRepositoryMethod() {
doCallRealMethod().when(mockedUserRepository).updateName(anyLong(), anyString());
userService.updateName(1L,"真實調用方法");
verify(mockedUserRepository, times(1)).add(1L,"真實調用方法");
}
- 公眾號FunTester首發,更多原創文章:FunTester430+原創文章,歡迎關注、交流,禁止第三方擅自轉載。
