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

为何显式声明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管理逻辑:

  1. 使用@Mock + MockitoExtension时的严格模式
    当测试类通过@ExtendWith(MockitoExtension.class)配合@Mock注解时,MockitoExtension会自动启用严格存根模式(StrictStubs)。此时,Mockito会跟踪当前测试上下文内创建的所有mock——包括你在ClassWithMockingInConstructor构造函数里用mock(CharSequence.class)生成的实例,因为这个mock是在测试方法的执行流程中创建的,属于当前测试上下文的一部分。

严格模式会校验所有存根的方法是否被实际调用,而你的测试只实例化了ClassWithMockingInConstructor,从未调用dependency.toString(),因此触发UnnecessaryStubbingException。

  1. 手动创建mock时的宽松模式
    当你把@Mock注解的EmptyClass改成在测试方法内手动调用mock(EmptyClass.class)创建时,MockitoExtension检测不到测试类中有@Mock、@InjectMocks等需要它管理的注解,因此不会激活严格存根的校验逻辑。此时Mockito默认使用宽松模式,不会检查存根方法是否被调用,自然不会抛出异常。

简单总结:

  • 用@Mock配合MockitoExtension:激活严格存根检查,所有测试上下文内的mock都会被校验存根的使用情况
  • 手动创建mock:MockitoExtension未激活严格模式,不对未使用的存根做校验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 09:23:24