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

Mockito调用openMocks后替换已注入Mock对象问题求助

问题描述

使用@RunWith(MockitoJUnitRunner.class)时,为@InjectMocks注解的对象调用openMocks()后,Mockito会替换已有的Mock对象。

代码示例:

@RunWith(MockitoJUnitRunner.class) 
public class MyTest {
    @Mock ClassA mockClassA;
    @Mock ClassB mockClassB;
    @InjectMocks ClassToTest classToTest;
    AutoCloseable closeable;
    
    @Before 
    public void openMocks() {
        closeable = MockitoAnnotations.openMocks(this);
    }
    //...
}

public class ClassToTest {
    @Inject private ClassA classA;
    private final ClassB classB;
    
    @Inject 
    public ClassToTest(ClassB classB) {
        this.classB = classB;
    }
}

注:无法修改ClassToTest的代码,不能将私有成员classA移到构造函数中。

测试时需要mockclassA和classB,由于classA不是构造函数注入,使用@InjectMocks让Mockito注入,确实成功注入了mockClassA。但classB出现问题:调用openMocks()前,classToTest.classB是mockClassB,符合预期;调用后classToTest.classB变成新的Mock对象,导致提前为mockClassB设置的返回值失效。

请问有没有办法让Mockito不替换classB的Mock对象?

解决方案

方法1:移除重复初始化逻辑

你同时使用了@RunWith(MockitoJUnitRunner.class)和MockitoAnnotations.openMocks(this),两者都是用来初始化Mock和@InjectMocks对象的,重复执行会触发Mockito重新创建实例并注入,直接覆盖原有的mockClassB。

只需保留其中一种初始化方式即可:

  • 要么删除@Before方法里的openMocks()调用,完全依赖MockitoJUnitRunner自动完成初始化;
  • 要么移除@RunWith(MockitoJUnitRunner.class)注解,仅保留@Before里的openMocks()(记得在@After方法中调用closeable.close()释放资源)。

方法2:手动注入classA替代@InjectMocks

如果因特殊原因必须保留两种初始化逻辑(不推荐),可以放弃@InjectMocks,通过反射手动注入classA,这样classB会始终保持原有的mockClassB实例:

@RunWith(MockitoJUnitRunner.class) 
public class MyTest {
    @Mock ClassA mockClassA;
    @Mock ClassB mockClassB;
    ClassToTest classToTest;
    AutoCloseable closeable;
    
    @Before 
    public void setup() throws NoSuchFieldException, IllegalAccessException {
        // 手动创建ClassToTest实例,传入已有的mockClassB
        classToTest = new ClassToTest(mockClassB);
        // 通过反射注入classA
        Field classAField = ClassToTest.class.getDeclaredField("classA");
        classAField.setAccessible(true);
        classAField.set(classToTest, mockClassA);
        
        closeable = MockitoAnnotations.openMocks(this);
    }
    
    @After
    public void teardown() throws Exception {
        closeable.close();
    }
    //...
}

方法3:调整@InjectMocks的构造注入优先级(Mockito 3.5+)

从Mockito 3.5.0版本开始,可以通过配置MockitoSettings调整@InjectMocks的注入策略,让它优先使用已存在的实例而非重新构造对象:

@Before 
public void openMocks() {
    closeable = MockitoAnnotations.openMocks(this, Mockito.withSettings()
        .injectMocksSettings(InjectMocksSettings.strictness(Strictness.LENIENT)
            .constructorInjectionPriority(ConstructorInjectionPriority.EXISTING_INSTANCE)));
}

这个配置会让Mockito复用已创建的classToTest实例(由MockitoJUnitRunner初始化的对象),仅注入缺失的classA成员,不会重新构造对象替换classB。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:05:30