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

