FunTester Mockito 框架 Mock Void 方法

FunTester · 2020年08月06日 · 1688 次阅读

在编写代码时,总是有方法返回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方法。因为,Mockitowhen()方法适用于返回值,而方法返回值是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+ 原创文章,欢迎关注、交流,禁止第三方擅自转载。

热文精选

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册