在編寫代碼時,總是有方法返回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+原創文章,歡迎關注、交流,禁止第三方擅自轉載。