如何替换@InjectMocks注入Mock为UTest局部变量Mock测试依赖为null场景
实现方案
你不需要删除原有类级别的Mock注解配置,仅需针对c2为null的测试场景做局部处理即可,原有其他测试用例可正常运行不受影响。
方案1:最小改动实现(推荐)
类层面的@InjectMocks、@Mock注解完全保留,仅在需要测试c2为null的测试方法中新建局部被测对象,不依赖类层面自动注入的实例:
// 原有类级别的注解配置保留,供其他普通测试用例使用 @InjectMocks private Class1 c1 = new Class1(); @Mock private Class2 c2; // 原有普通测试用例无需修改,正常运行 @Test public void testNormalBiz() { when(c2.query()).thenReturn("mockVal"); Assertions.assertEquals("mockVal", c1.doBiz()); } // 专门测试c2为null的场景 @Test public void testWhenC2IsNull() { // 局部新建被测对象,不使用类层面被注入过的c1实例 Class1 testInstance = new Class1(); // 不做任何c2注入操作,此时testInstance内部的c2成员默认就是null // 执行目标方法,验证null场景下的预期逻辑,比如抛出异常、走降级分支等 Assertions.assertThrows(NullPointerException.class, () -> testInstance.doBiz()); }
注意:不要直接把类层面共享的c1实例中的c2属性手动设为null,单元测试执行顺序不固定,会导致其他共用该实例的用例出现偶发失败,局部新建对象是最稳妥的隔离方式。
方案2:全局部变量控制(适合重构测试类的场景)
如果不想用类级别的注解注入,所有测试用例都可以通过局部构造Mock、手动注入的方式实现,灵活控制每个用例的依赖状态:
// 类层面不再定义@InjectMocks、@Mock修饰的共享成员变量 public class Class1Test { // 普通用例:手动注入Mock的c2 @Test public void testNormalBiz() { // 局部构造Mock对象 Class2 mockC2 = Mockito.mock(Class2.class); when(mockC2.query()).thenReturn("mockVal"); // 构造被测对象 Class1 testInstance = new Class1(); // 手动注入依赖,如果没有公开setter,用反射工具注入即可 ReflectionTestUtils.setField(testInstance, "c2", mockC2); // 执行测试断言 Assertions.assertEquals("mockVal", testInstance.doBiz()); } // c2为null的测试用例 @Test public void testWhenC2IsNull() { Class1 testInstance = new Class1(); // 不注入c2,成员默认null,直接执行逻辑断言即可 Assertions.assertThrows(NullPointerException.class, () -> testInstance.doBiz()); } }
如果你的测试类没有引入Spring相关依赖,也可以用纯JDK反射或者Mockito自带的工具完成私有属性注入,不需要为了测试修改业务代码新增setter方法。
内容的提问来源于stack exchange,提问作者Eden Berdugo
相关产品推荐
相关产品推荐

