积极心态,精细目标,时间管理,知行合一,求知创新 声明:本博客主要用来收藏精品帖子和个人工作总结,如有涉及版权,请联系本人删贴。 关注:java,python和groovy开发语言,单元,接口,安全,自动化,性能等测试技术,系统架构,分布式,中间件,微服务,大数据,云技术、人工智能等 愿自己不断保持阅读、成长、思考、经历,成就更好的自己......

转载:https://unmi.cc/mockito-how-to-mock-void-method/#more-7748

最初接触 Mockito 还思考并尝试过如何用它来 mock 返回值为 void 的方法,然而 Google 查找到的一般都会说用 doThrow() 的办法

doThrow(new RuntimeException()).when(mockObject).methodWithVoidReturn();

因为无法使用常规的 when(mockObject.foo()).thenReturn(...) 的方法。

当时我就纳闷,为何我想 mock 一个返回值为 void 的方法,却是在模拟抛出一个异常,现在想来如果一个返回值为 void 的方法,为何要去 mock 这个方法呢?

回想一个我们要 mock 一个方法的意图是什么:

  • 在特定输入参数的情况下期待需要的输出结果(返回值)
  • 在方法抛出某种类型异常调用者作出的反应
  • 对于 void 返回值的方法,如果要验证有没有被调用过几次可以在事后用 verify() 方法去断言。所以基本上对于 void 返回值的方法一般可不用去 mock 它,只需用  verify() 去验证,或者就是像前面一样模拟出现异常时的情况。

    所以本文并不像是去直接回答标题所示的问题: Mockito 如何 mock  返回值为  void 的方法,而是如何应对 mock  对象的  void 方法

    一般不用对 void 方法打桩, 事后 verify 就行

    测试代码针对 mock  对象的 void 方法调用本来就没有什么效果,所以一般也无须用 doNothing() , 况且 void 提供不了返回值作进一步 mock,只需要在事后用 verify() 进行验证一下。

    例如有下面的代码

    类 UserDao

    @ Test
    public void shouldCallUserDaoSaveMethod ( ) {
    UserDao userDao = Mockito . mock ( UserDao . class ) ;
    UserService userService = new UserService ( userDao ) ;
    User user = new User ( 1 , "Yanbin" ) ;
    userService . saveUser ( user ) ;
    verify ( userDao , times ( 1 ) ) . save ( user ) ;
    userDao . save ( user ) ;
    catch ( DataAccessException dae ) {
    throw new ApplicationException ( dae , "can't save user due to issue reported from database" ) ;
    @ Test ( expected = ApplicationException . class )
    public void raiseApplicationExceptionIfDataAccessExceptionOccursFromUserDao ( ) {
    UserDao userDao = Mockito . mock ( UserDao . class ) ;
    UserService userService = new UserService ( userDao ) ;
    User user = new User ( 1 , "Yanbin" ) ;
    doThrow ( new DataAccessException ( ) ) . when ( userDao ) . save ( user ) ;
    userService . saveUser ( user ) ;

    上面的 doThrow() 行写成下面的方式也是一样的

    doNothing().doThrow(new DataAccessException()).when(userDao).save(user);

    Mockito 在调用 mock  对象的  void 方法时本来默认就是 doNothing . 那什么时候需要用到 doNothing() 呢?就是下面的情况

    部分 mock 时 void 方法可 doNothing

    除了 @Mock Mockito.mock(Class<T>) 来创建 mock 对象,还可以使用 spy(realObject) spy(Class) 来获得 mock 对象,这两种方式得到的 mock  对象是不同的。前者所有的方法都被 mock, 后者(spy) 未打桩的方法会调用被 spy 对象的实际实现。

    看下面的例子如何对 spy 对象的方法进行 mock  的

    @ Test
    public void partialMockSpiedObject ( ) {
    List < String > names = new ArrayList <> ( ) ;
    List < String > spy = spy ( names ) ;
    doNothing ( ) . when ( spy ) . add ( anyInt ( ) , anyString ( ) ) ;
    spy . add ( "Yanbin" ) ;
    spy . add ( 0 , "Unmi" ) ;
    assertEquals ( 1 , spy . size ( ) ) ;
    assertEquals ( "Yanbin" , spy . get ( 0 ) ) ;
    assertEquals ( 0 , names . size ( ) ) ;

    上面的测试可成功通过,我们用 doNothing().when(spy).add(anyInt(), anyString()) 让带下标的方式添加元素什么也不做,而未被打桩的方法会调用 names 的实际实现。因此我们可以看到上面的断言结果。

    doNothing() 还真不是 doNothing() , 在搭配 spy 作部分 mock 时还是很有用的,如下面的测试

    @ Test
    public void testHandleEvent ( ) {
    Handler handler = spy ( new Handler ( ) ) ; //spy 会是部分 mock, 只有 stub 了方法才会被 mock, 其余调用实际方法
    //放心的用反射去修改 userDao 的值,因为不会被 initialize() 方法覆盖
    UserDao mockedUserDao = Mockito . mock ( UserDao ) ;
    setFieldValueOfUserDaoInReflection ( handler , mockedUserDao ) ;
    doNothing ( ) . when ( handler ) . initialize ( ) ; //因为它 doNothing, 保证了 handler 仍然持有 mockedUserDao
    when ( mockedUserDao . findUsers ( ) ) . thenReturn ( users ) ;
    . . . . . .

    另外如果还有其他情况可考虑 doCallRealMethod() 或  doAnser()

    doCallRealMethod().when(mockObject).voidMethod();
    doAnswer(answer).when(mockObject).voidMethod();

    相关链接:

  • Mockito: How to mock a void method call
  • How to make mock to void methods with mockito
  • UserService service = new UserService ( ) ;
    UserService spyService = spy ( service ) ;
    doReturn ( "Yanbin" ) . when ( spyService ) . findNameById ( 123 ) ;
    // spy 部分 mock 不能下面那样
    // when(spyService.findNameById(123)).thenReturn("Yanbin");
    // 否则 when(..) 打桩的时候就会执行实际的方法 service.findNameById(123),这是我们不希望的