在编写代码时,总是有方法返回void
,并且在某个测试用例需要模拟void
方法。那么我们如何去做呢?让我们一起在下面的内容中使用Mockito
完成这个需求。
Mockito
是用于编写单元测试的最著名的模拟框架之一。假设我们有一个方法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
方法。根据要求使用其中一个选项:
doNothing()
:完全忽略对void
方法的调用,这是默认doAnswer()
:在调用void
方法时执行一些运行时或复杂的操作doThrow()
:调用模拟的 void
方法时引发异常doCallRealMethod()
:不要模拟并调用真实方法如果我们只想完全忽略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()
。
下面是使用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()
方法。
让我们举一个例子:当使用null
作为id
调用updateName()
方法时,我们将引发InvalidParamException
。
@Test(expected = InvalidParamException.class)
public void testUpdateNameThrowExceptionWhenIdNull() {
doThrow(new InvalidParamException())
.when(mockedUserRepository).updateName(null,anyString();
userService.updateName(null,"FunTester");
}
有时有必要从模拟对象中调用真实方法,在这种情况下,我们需要使用doCallRealMethod()
,因为doNothig()
是默认行为。
在以下示例中,即使是模拟对象,也会调用userRepository
中的真实方法。
@Test
public void testUpdateNameCallRealRepositoryMethod() {
doCallRealMethod().when(mockedUserRepository).updateName(anyLong(), anyString());
userService.updateName(1L,"真实调用方法");
verify(mockedUserRepository, times(1)).add(1L,"真实调用方法");
}