
本文介绍在不使用 PowerMock 的前提下,利用 Mockito 5.2+ 的 MockedConstruction 正确模拟构造函数抛出异常的方法,并推荐更符合测试最佳实践的重构方案——通过可注入工厂替代直接构造调用。
本文介绍在不使用 powermock 的前提下,利用 mockito 5.2+ 的 `mockedconstruction` 正确模拟构造函数抛出异常的方法,并推荐更符合测试最佳实践的重构方案——通过可注入工厂替代直接构造调用。
Mockito 自 3.4.0 起引入了 MockedConstruction
因此,你最初尝试的 when(??).thenThrow(ex) 中的 ?? 实际上不存在合法的 stub 目标;构造行为必须在 MockInitializer 或 Answer 中提前声明,而非事后配置。
✅ 正确用法(推荐):使用 MockedConstruction + MockInitializer(简洁 Lambda 版)
@Test
void exceptionAtConstruction() {
RuntimeException ex = new RuntimeException("simulated construction failure");
try (MockedConstruction<someclass> ignored =
Mockito.mockConstruction(SomeClass.class,
(mock, context) -> { throw ex; })) {
assertThrows(RuntimeException.class, () -> new SomeClass());
}
}</someclass>
⚠️ 注意事项:
- MockedConstruction 是 try-with-resources 资源,必须在 try 块内执行 new SomeClass(),否则拦截不生效;
- 抛出的异常会作为 RuntimeException 的直接原因(cause),而非原始异常本身(因 Mockito 将其包装为 MockConstructionException)。若需严格匹配异常类型与消息,建议使用 assertThrows 的返回值做进一步断言:
Throwable thrown = assertThrows(RuntimeException.class, () -> new SomeClass()); assertEquals("simulated construction failure", thrown.getMessage());
? 更优解:重构代码,避免构造函数测试困境(强烈推荐)
Mockito 官方明确建议:“Don’t mock a type you don’t own”(来源)。直接 mock 第三方或不可控类的构造函数,本质是测试脆弱性高、违反封装的设计。
✅ 推荐做法:引入可替换的工厂抽象
class YourService {
// 可测试性入口:默认使用真实构造器,测试时可替换
private final Supplier<someclass> someClassFactory;
// 生产构造器
public YourService() {
this(SomeClass::new);
}
// 测试专用构造器
@VisibleForTesting
YourService(Supplier<someclass> factory) {
this.someClassFactory = factory;
}
public void doSomething() {
SomeClass instance = someClassFactory.get(); // ← 构造行为被解耦
// ... use instance
}
}</someclass></someclass>
对应测试(零依赖 Mockito):
@Test
void doSomething_throwsOnConstruction() {
Supplier<someclass> failingFactory = () -> {
throw new RuntimeException("boom");
};
YourService service = new YourService(failingFactory);
assertThatThrownBy(service::doSomething)
.isInstanceOf(RuntimeException.class)
.hasMessage("boom");
}</someclass>
? 总结:
- MockedConstruction 是可行的技术方案,适用于临时绕过构造异常场景,但应谨慎使用;
- 真正可持续的解决方案是面向测试重构:将构造逻辑提取为策略(Supplier
、Function<...> 或接口),实现关注点分离; - 这不仅使构造异常测试变得简单直接,还提升了代码的可维护性、可扩展性与设计清晰度。











