如何在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
相关产品推荐
相关产品推荐

