
在 spring batch 中,步骤内抛出的异常不会直接传播到测试调用栈,而是被封装进 `jobexecution.failureexceptions` 集合;因此直接用 `assertthrows` 会失败,需通过检查 `jobexecution` 的失败异常列表来验证。
Spring Batch 的执行模型具有强事务与容错语义:当 Tasklet.execute() 或任何步骤组件(如 ItemReader/ItemProcessor)抛出未捕获异常时,框架会捕获该异常、回滚当前事务、记录错误,并将异常添加至 JobExecution 实例的 failureExceptions 属性中,而非向上抛出。这意味着 jobLauncherTestUtils.launchJob(...) 方法正常返回一个 JobExecution 对象(状态通常为 FAILED),而不会触发 IllegalStateException 等原始异常——这正是 assertThrows 断言失败的根本原因。
要正确验证异常是否按预期发生,应改为:
- 调用 launchJob() 获取 JobExecution;
- 断言其 getStatus() 为 FAILED;
- 检查 failureExceptions 是否包含目标异常类型(支持嵌套异常匹配)。
以下是推荐的测试写法(使用 JUnit 5 + AssertJ):
import static org.assertj.core.api.Assertions.*;
@Test
void testJobThrowsMyException() {
// given
JobParameters jobParameters = new JobParameters();
// when
JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);
// then
assertThat(jobExecution.getStatus()).isEqualTo(BatchStatus.FAILED);
assertThat(jobExecution.getFailureExceptions())
.anySatisfy(throwable -> {
assertThat(throwable)
.isInstanceOf(IllegalStateException.class)
.hasMessage("Thats the exception i want to test");
});
}⚠️ 注意事项:
- jobLauncherTestUtils.launchJob() 总是返回 JobExecution,即使作业失败也不会抛出异常(除非启动阶段出错,如 JobInstanceAlreadyCompleteException);
- failureExceptions 是一个 List<Throwable>,可能包含多个异常(例如重试后仍失败、或嵌套的 RuntimeException 包装);
- 若使用 @EnableBatchProcessing 默认配置,JobLauncherTestUtils 会自动注入并复用测试上下文中的 JobLauncher 和 JobRepository,确保行为与真实环境一致;
- 不建议手动遍历 failureExceptions 并用 instanceof 做简单判断——应借助 AssertJ 的 anySatisfy 或 filteredOn 等语义化断言,提升可读性与健壮性。
总结:Spring Batch 的异常处理是声明式、面向作业生命周期的。测试时必须“面向 JobExecution 编程”,而非假设异常会穿透调用链。掌握 failureExceptions 的语义,是编写可靠批处理单元测试的关键前提。










