mockito 中 dothrow().when() 模拟异常不依赖方法是否声明 throws,但受检异常必须匹配签名才能通过编译;void 方法用 dothrow,有返回值方法推荐 when().thenthrow。

在 Mockito 中,doThrow().when() 用于模拟方法抛出异常,它不依赖于被测方法是否用 throws 声明了异常 —— Mockito 关心的是运行时行为,而不是编译时声明。也就是说,即使目标方法签名里写了 throws IOException,你照样可以用 doThrow() 模拟它;反过来,即使没声明异常,也能强行抛出(但要注意:对受检异常,必须匹配声明,否则编译不过)。
理解 throws 和 doThrow 的关系
throws 是 Java 方法签名的一部分,告诉调用者“这个方法可能抛出这些异常”,主要用于编译检查(尤其是受检异常)。而 doThrow().when() 是 Mockito 在测试中控制**实际执行时的行为**。Mockito 不会校验你抛的异常是否在原方法的 throws 列表里,但它会影响编译:
- 如果原方法声明了
throws IOException,你用doThrow(new IOException())是合法且安全的; - 如果原方法没声明抛出
IOException(且IOException是受检异常),直接doThrow(new IOException())会导致编译错误; - 此时应改用运行时异常(如
RuntimeException),或确保 mock 的方法签名支持该受检异常。
正确写法:匹配声明的受检异常
假设被测类有这样一个方法:
public void readFile() throws IOException { ... }
在测试中 mock 它抛出 IOException:
FileReader reader = mock(FileReader.class);
doThrow(new IOException("file not found")).when(reader).readLine();
✅ 合法,因为 readLine() 原本就声明抛出 IOException。
常见错误与绕过方式
如果要 mock 一个**没声明抛异常**的方法(比如 void save()),又想让它抛受检异常,编译会报错。这时有几种处理方式:
- 改用运行时异常:
doThrow(new RuntimeException("db failed")).when(service).save(); - 借助
thenThrow()(适用于有返回值的方法,且更常用在 when().thenThrow 链式调用中); - 如果必须用受检异常,且无法改方法签名,可考虑用
PowerMockito(不推荐,增加复杂度),或重构被测代码使其更易测。
注意 void 方法和非 void 方法的区别
doThrow() 主要用于 void 方法;对有返回值的方法,优先用 when().thenThrow() 更直观:
// void 方法 → 用 doThrow doThrow(new SQLException()).when(dao).deleteUser(123); // 有返回值方法 → 推荐用 when().thenThrow when(service.getData()).thenThrow(new TimeoutException());
两者底层等价,但 doThrow 避免了 void 方法调用返回值导致的编译错误(比如误写成 when(mock.voidMethod()).thenThrow(...) 会报错)。
不复杂但容易忽略:Mockito 的异常模拟只作用于 mock 对象,不影响真实对象;确保你 mock 的是真正被调用的那个实例,而不是 new 出来的原始对象。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











