You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Mockito测试中验证方法调用顺序且避免UnnecessaryStubbingException?

解决Mockito严格模式下验证授权与读取操作顺序的问题

核心思路

你的问题本质是:未授权场景下,授权检查会直接抛出异常,导致后续读取操作不会执行,因此之前对entityReader.readMyEntity的存根属于多余操作,触发了Mockito严格模式的UnnecessaryStubbingException。我们可以通过验证读取操作未被执行来间接证明授权检查的优先性,同时避免多余存根。

方案一:未授权场景测试(验证读取操作未触发)

直接去掉对entityReader的多余存根,转而验证它没有任何交互。这样既符合严格模式要求,又能证明授权检查先执行(如果读取先执行,entityReader必然会被调用,验证会失败)。

测试代码示例:

@Test
void getEntity_WhenUnauthorized_ThrowsAccessDeniedAndSkipsReading() {
    // 存根授权检查抛出异常
    doThrow(new AccessDeniedException()).when(authorityChecker).assertHasReadAuthorization();

    // 执行目标方法并断言预期异常
    assertThrows(AccessDeniedException.class, () -> cut.getEntity("1234"));

    // 验证实体读取器未被调用,证明授权检查优先执行
    verifyNoInteractions(entityReader);
}

方案二:授权通过场景测试(验证执行顺序)

对于授权通过的正常场景,可以使用InOrder来明确验证执行顺序,此时对entityReader的存根是必要的,不会触发多余存根异常。

测试代码示例:

@Test
void getEntity_WhenAuthorized_ExecutesAuthorizationBeforeReading() {
    // 存根授权检查正常通过
    doNothing().when(authorityChecker).assertHasReadAuthorization();
    // 存根读取操作返回模拟实体
    MyEntity mockEntity = new MyEntity();
    when(entityReader.readMyEntity("1234")).thenReturn(mockEntity);

    // 执行目标方法
    MyEntity result = cut.getEntity("1234");

    // 验证操作顺序:先授权检查,再读取实体
    InOrder executionOrder = inOrder(authorityChecker, entityReader);
    executionOrder.verify(authorityChecker).assertHasReadAuthorization();
    executionOrder.verify(entityReader).readMyEntity("1234");

    // 验证返回结果符合预期(根据实际映射逻辑调整)
    assertEquals(mockEntity, result);
}

为什么这两个方案可行?

  • 未授权场景:通过verifyNoInteractions确保读取操作完全没被触发,这就间接证明了授权检查的执行顺序在读取之前——如果读取先执行,readMyEntity必然会被调用,验证会直接失败。
  • 授权通过场景:InOrder的使用是合理的,因为我们确实需要确保授权通过后才允许读取敏感数据,这种耦合是业务逻辑的必要保障,不属于过度耦合。

这样既保留了Mockito的严格模式,又精准验证了业务所需的执行顺序。

内容的提问来源于stack exchange,提问作者Florian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 17:53:42