使用@InjectMocks遇构造方法抛异常,如何正确注入Mock?
解决方案
方案1:在测试初始化方法中处理异常并完成注入
不用在字段声明时直接new MyObject(),而是把实例创建放到测试的初始化方法(JUnit 5用@BeforeEach,JUnit 4用@Before)里,方法声明抛出异常或者捕获异常,同时让Mockito完成注入:
public class MyObjectTest { @Mock(name = "random") Random mockRandom; MyObject myObject; @BeforeEach void setUp() throws Exception { // 初始化Mockito注解 MockitoAnnotations.openMocks(this); // 在这里创建实例,方法声明throws Exception解决IDE报错 myObject = new MyObject(); // Mockito会自动把mockRandom注入到myObject的private字段中 } // 你的测试方法写在这里 }
这种方法不用修改生产代码,只调整测试类的写法即可,最省事。
方案2:给生产类加一个测试专用的构造函数
在MyObject里新增一个包级私有(或者测试可见)的构造函数,专门用来传入Random实例,原有的无参构造保持不变,业务代码依然用无参构造创建实例,测试用这个专用构造:
首先修改MyObject:
public class MyObject { private Random random; // 其他属性 // 业务用的无参构造 public MyObject() throws Exception { this(new Random()); } // 测试专用构造,包级私有,同包测试类可以访问 MyObject(Random random) throws Exception { this.random = random; // 把原来构造里的其他逻辑抽成统一的初始化方法,避免代码重复 init(); } private void init() throws Exception { // 原来无参构造里除了初始化random的其他代码 } }
然后测试类直接用这个构造传mock:
public class MyObjectTest { @Mock Random mockRandom; MyObject myObject; @BeforeEach void setUp() throws Exception { // 直接传入mock的Random myObject = new MyObject(mockRandom); } // 测试方法 }
这种方式把测试代码和生产代码的耦合降到最低,业务代码完全不受影响,同时测试时能灵活注入mock,是比较优雅的做法。
方案3:手动调用Mockito的注入方法
如果不想改生产代码,也可以手动创建实例后,调用Mockito的injectMocks方法完成注入,同时处理构造异常:
public class MyObjectTest { @Mock(name = "random") Random mockRandom; MyObject myObject; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); try { myObject = new MyObject(); // 手动触发注入 Mockito.injectMocks(myObject); } catch (Exception e) { // 把异常包装成运行时异常抛出,不中断测试流程 throw new RuntimeException("创建MyObject实例失败", e); } } // 测试方法 }
这个方案和方案1类似,区别在于手动调用注入方法,适合一些特殊场景。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

