如何通过JUnit的assertThrows验证业务代码正确抛出指定异常?

2026-04-30 17:041阅读0评论SEO资讯
  • 内容介绍
  • 相关推荐

本文共计807个文字,预计阅读时间需要4分钟。

如何通过JUnit的assertThrows验证业务代码正确抛出指定异常?

要验证一段业务代码是否如预期那样抛出了特定异常,可以使用JUnit 5提供的`assertThrows`方法。这个方法不仅可以确认异常被抛出,还可以获取异常实例,用于进一步分析(如检查异常消息、错误代码等)。与旧版本的`@Test(expected=...)`注解相比,`assertThrows`更加灵活和安全性高。

例如:

基础用法:确认异常类型被抛出

最常用场景是检查某段逻辑是否在非法输入或状态时触发了目标异常:

  • 传入一个 Class<E> 类型参数指定期望的异常类(如 IllegalArgumentException.class
  • 第二个参数是函数式接口 Executable,封装待测的“可能抛异常”的代码块
  • 方法返回捕获到的异常对象,可用于后续校验

示例:

assertThrows(IllegalArgumentException.class, () -> userService.updateUser(null));

进阶断言:校验异常消息与字段

仅确认异常类型不够,业务中常需验证异常携带的提示信息或业务错误码是否准确。此时可先捕获异常,再对其属性做断言:

  • assertThrows 的返回值赋给变量,避免重复执行被测逻辑
  • assertEquals 检查 e.getMessage() 是否匹配预期文案
  • 若自定义异常含 errorCodetimestamp 等字段,可直接调用 getter 断言

示例:

IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.updateUser(new User("", "123")));
assertEquals("用户名不能为空", e.getMessage());

注意边界:避免误判和空指针

assertThrows 本身不处理“未抛异常”的情况——如果代码正常执行完毕,测试会直接失败(报错 “Expected java.lang.IllegalArgumentException to be thrown”)。但有两点易被忽略:

  • 确保被测代码确实执行到了可能抛异常的路径(例如 if 分支、数据库连接失败模拟等),必要时配合 Mockito 打桩控制流程
  • 不要对返回值为 null 的异常对象做链式调用(如 e.getMessage().contains(...)),应先确认异常非 null(assertThrows 已保证不为 null,但自定义逻辑中仍需谨慎)

替代方案对比:为什么不用 @Test(expected = ...)

JUnit 4 的 @Test(expected = ...) 注解存在明显局限:

  • 只能声明异常类型,无法获取异常实例,不能校验消息或字段
  • 只要测试方法中任意位置抛出该异常即算通过,哪怕发生在断言之后或清理逻辑里,容易掩盖真实问题
  • 不支持 lambda 表达式,写法冗长,可读性差

assertThrows 将校验范围精确限定在你指定的代码块内,语义清晰,扩展性强,是当前推荐的标准做法。

本文共计807个文字,预计阅读时间需要4分钟。

如何通过JUnit的assertThrows验证业务代码正确抛出指定异常?

要验证一段业务代码是否如预期那样抛出了特定异常,可以使用JUnit 5提供的`assertThrows`方法。这个方法不仅可以确认异常被抛出,还可以获取异常实例,用于进一步分析(如检查异常消息、错误代码等)。与旧版本的`@Test(expected=...)`注解相比,`assertThrows`更加灵活和安全性高。

例如:

基础用法:确认异常类型被抛出

最常用场景是检查某段逻辑是否在非法输入或状态时触发了目标异常:

  • 传入一个 Class<E> 类型参数指定期望的异常类(如 IllegalArgumentException.class
  • 第二个参数是函数式接口 Executable,封装待测的“可能抛异常”的代码块
  • 方法返回捕获到的异常对象,可用于后续校验

示例:

assertThrows(IllegalArgumentException.class, () -> userService.updateUser(null));

进阶断言:校验异常消息与字段

仅确认异常类型不够,业务中常需验证异常携带的提示信息或业务错误码是否准确。此时可先捕获异常,再对其属性做断言:

  • assertThrows 的返回值赋给变量,避免重复执行被测逻辑
  • assertEquals 检查 e.getMessage() 是否匹配预期文案
  • 若自定义异常含 errorCodetimestamp 等字段,可直接调用 getter 断言

示例:

IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.updateUser(new User("", "123")));
assertEquals("用户名不能为空", e.getMessage());

注意边界:避免误判和空指针

assertThrows 本身不处理“未抛异常”的情况——如果代码正常执行完毕,测试会直接失败(报错 “Expected java.lang.IllegalArgumentException to be thrown”)。但有两点易被忽略:

  • 确保被测代码确实执行到了可能抛异常的路径(例如 if 分支、数据库连接失败模拟等),必要时配合 Mockito 打桩控制流程
  • 不要对返回值为 null 的异常对象做链式调用(如 e.getMessage().contains(...)),应先确认异常非 null(assertThrows 已保证不为 null,但自定义逻辑中仍需谨慎)

替代方案对比:为什么不用 @Test(expected = ...)

JUnit 4 的 @Test(expected = ...) 注解存在明显局限:

  • 只能声明异常类型,无法获取异常实例,不能校验消息或字段
  • 只要测试方法中任意位置抛出该异常即算通过,哪怕发生在断言之后或清理逻辑里,容易掩盖真实问题
  • 不支持 lambda 表达式,写法冗长,可读性差

assertThrows 将校验范围精确限定在你指定的代码块内,语义清晰,扩展性强,是当前推荐的标准做法。