
在 spring batch 中,步骤内抛出的异常不会直接传播到测试调用栈,而是被封装进 `jobexecution.failureexceptions` 集合;因此需通过检查该集合而非 `assertthrows` 来验证异常是否发生。
Spring Batch 的执行模型具有强事务与容错语义:当 Tasklet.execute() 方法抛出未捕获异常(如 IllegalStateException)时,框架会主动捕获该异常,执行事务回滚,并将异常记录在 JobExecution 实例的 failureExceptions 属性中,而非向上抛出至 jobLauncherTestUtils.launchJob() 调用点。这正是 assertThrows 失败的根本原因——它等待的是方法调用栈中显式抛出的异常,而实际返回的是一个已“消化”异常的 JobExecution 对象。
要正确验证异常行为,应改为获取并检查 JobExecution 实例。以下是推荐的断言方式(使用 JUnit 5 + AssertJ):
@Test
void testJobThrowsMyException() {
// given
JobParameters jobParameters = new JobParameters();
// when
JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);
// then
assertThat(jobExecution.getFailureExceptions())
.hasSize(1)
.first()
.isInstanceOf(IllegalStateException.class)
.hasMessage("Thats the exception i want to test");
}✅ 关键点说明: jobLauncherTestUtils.launchJob(...) 总是返回 JobExecution(即使失败),从不抛出原始业务异常; JobExecution.getFailureExceptions() 返回 List<Throwable>,按发生顺序保存所有步骤级致命异常; 若需验证多异常场景(如重试后仍失败),可进一步检查异常链或嵌套原因(getCause())。
⚠️ 注意事项:
- 确保 jobLauncherTestUtils 已正确定义为 @Autowired 的 JobLauncherTestUtils Bean(通常由 @SpringBatchTest 启用);
- 不要尝试在测试中 catch 异常或手动调用 jobExecution.getStatus() 判断 FAILED——failureExceptions 非空即表明执行失败,语义更精确;
- 若任务步配置了 skip 或 retry 策略,异常可能被吞没或重试,此时需确认 Tasklet 所在 Step 未启用相关容错机制,否则异常不会进入 failureExceptions。
综上,Spring Batch 测试的核心原则是:以 JobExecution 为第一手观测对象,而非依赖传统方法调用异常传播逻辑。这一设计保障了批处理作业的健壮性,也要求开发者在测试层面同步适配其声明式错误处理模型。










