为何显式声明Mock会让别处的Stubbing隐式宽松?
问题分析:Mockito不必要Stubbing异常的诡异场景
这一现象十分奇怪,以下是最小可复现示例(MRE),测试执行失败:
package com.example.dynamicgateway.service.routeProcessor; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class MockingTest { @Mock private EmptyClass emptyClass; @Test void test() { new ClassWithMockingInConstructor(); } private static class EmptyClass { } private static class ClassWithMockingInConstructor { Object dependency; ClassWithMockingInConstructor() { dependency = mock(CharSequence.class); when(dependency.toString()).thenReturn("whatever"); } } }
执行测试后抛出异常:
org.mockito.exceptions.misusing.UnnecessaryStubbingException: Unnecessary stubbings detected. Clean & maintainable test code requires zero unnecessary code. Following stubbings are unnecessary (click to navigate to relevant line of code): 1. -> at com.example.dynamicgateway.service.routeProcessor.MockingTest$ClassWithMockingInConstructor.<init>(MockingTest.java:28) Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class. at org.mockito.junit.jupiter.MockitoExtension.afterEach(MockitoExtension.java:192) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
将when(dependency.toString()).thenReturn("whatever");替换为lenient().when(dependency.toString()).thenReturn("whatever");可解决问题(符合预期)。但另一种方式也能解决问题:将使用@Mock注解声明的EmptyClass mock改为在测试方法内显式调用mock(EmptyClass.class)创建,此时Mockito不再报错。这究竟是怎么回事?为何将声明式Mock改为显式创建,会让完全无关位置的Stubbing隐式变为宽松模式?
环境:Spring Boot 3.2.1,使用spring-boot-starter-test依赖。
原因解析
核心在于Mockito严格模式的触发条件和测试上下文的mock管理逻辑:
- 使用@Mock + MockitoExtension时的严格模式
当测试类通过@ExtendWith(MockitoExtension.class)配合@Mock注解时,MockitoExtension会自动启用严格存根模式(StrictStubs)。此时,Mockito会跟踪当前测试上下文内创建的所有mock——包括你在ClassWithMockingInConstructor构造函数里用mock(CharSequence.class)生成的实例,因为这个mock是在测试方法的执行流程中创建的,属于当前测试上下文的一部分。
严格模式会校验所有存根的方法是否被实际调用,而你的测试只实例化了ClassWithMockingInConstructor,从未调用dependency.toString(),因此触发UnnecessaryStubbingException。
- 手动创建mock时的宽松模式
当你把@Mock注解的EmptyClass改成在测试方法内手动调用mock(EmptyClass.class)创建时,MockitoExtension检测不到测试类中有@Mock、@InjectMocks等需要它管理的注解,因此不会激活严格存根的校验逻辑。此时Mockito默认使用宽松模式,不会检查存根方法是否被调用,自然不会抛出异常。
简单总结:
- 用
@Mock配合MockitoExtension:激活严格存根检查,所有测试上下文内的mock都会被校验存根的使用情况 - 手动创建mock:MockitoExtension未激活严格模式,不对未使用的存根做校验
内容的提问来源于stack exchange,提问作者Sergey Zolotarev
相关产品推荐
相关产品推荐

