Spring Batch中实现StepExecutionListener的Tasklet单元测试方法咨询
测试实现StepExecutionListener的Spring Batch Tasklet
要对这类Tasklet做纯单元测试,核心是用Mockito模拟Spring Batch的核心对象(比如StepExecution、JobExecution、ExecutionContext),不需要启动Spring容器,直接针对Tasklet的各个方法独立验证逻辑。
依赖准备
确保测试依赖包含:
- JUnit 5(JUnit Jupiter)
- Mockito Core
- Mockito JUnit Jupiter(用于集成Mockito和JUnit)
测试类示例
以下是针对MyTasklet的完整单元测试:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.batch.core.*; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.scope.context.StepContext; import java.io.File; import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class MyTaskletTest { @InjectMocks private MyTasklet myTasklet; @Mock private StepExecution stepExecution; @Mock private JobExecution jobExecution; @Mock private ExecutionContext executionContext; @Mock private StepContribution stepContribution; @Mock private ChunkContext chunkContext; @Mock private StepContext stepContext; @Test void beforeStep_ShouldRetrieveInputFilesFromExecutionContext() { // 准备测试数据 List<File> testFiles = List.of(new File("test1.txt"), new File("test2.txt")); // 模拟依赖链 when(stepExecution.getJobExecution()).thenReturn(jobExecution); when(jobExecution.getExecutionContext()).thenReturn(executionContext); when(executionContext.get("inputs")).thenReturn(testFiles); // 执行方法 myTasklet.beforeStep(stepExecution); // 验证:通过反射获取inputFiles并检查是否正确赋值(因为是私有变量) try { var inputFilesField = MyTasklet.class.getDeclaredField("inputFiles"); inputFilesField.setAccessible(true); List<File> actualFiles = (List<File>) inputFilesField.get(myTasklet); assertEquals(testFiles, actualFiles); } catch (NoSuchFieldException | IllegalAccessException e) { fail("反射获取inputFiles失败", e); } } @Test void execute_ShouldProcessAllInputFiles() throws Exception { // 准备测试数据 List<File> testFiles = List.of(new File("test1.txt"), new File("test2.txt")); // 使用Spy来监控MyTasklet,以便验证私有方法调用 MyTasklet spyTasklet = spy(new MyTasklet()); // 反射设置inputFiles(跳过beforeStep直接测试execute) var inputFilesField = MyTasklet.class.getDeclaredField("inputFiles"); inputFilesField.setAccessible(true); inputFilesField.set(spyTasklet, testFiles); // 模拟chunkContext的必要依赖 when(chunkContext.getStepContext()).thenReturn(stepContext); when(stepContext.getStepExecution()).thenReturn(stepExecution); // 执行方法 RepeatStatus result = spyTasklet.execute(stepContribution, chunkContext); // 验证结果 assertEquals(RepeatStatus.FINISHED, result); // 验证每个文件都调用了parseFile方法 verify(spyTasklet, times(testFiles.size())).parseFile(any(File.class)); } @Test void execute_ShouldThrowRuntimeExceptionWhenParseFileFails() throws Exception { // 准备测试数据 File testFile = new File("error.txt"); MyTasklet spyTasklet = spy(new MyTasklet()); // 反射设置inputFiles var inputFilesField = MyTasklet.class.getDeclaredField("inputFiles"); inputFilesField.setAccessible(true); inputFilesField.set(spyTasklet, List.of(testFile)); // 模拟parseFile抛出异常 doThrow(new RuntimeException("解析失败")).when(spyTasklet).parseFile(testFile); // 模拟chunkContext依赖 when(chunkContext.getStepContext()).thenReturn(stepContext); when(stepContext.getStepExecution()).thenReturn(stepExecution); // 验证异常 RuntimeException exception = assertThrows(RuntimeException.class, () -> spyTasklet.execute(stepContribution, chunkContext)); assertEquals("解析失败", exception.getMessage()); } @Test void afterStep_ShouldPutOutputsToExecutionContext() { // 模拟依赖链 when(stepExecution.getJobExecution()).thenReturn(jobExecution); when(jobExecution.getExecutionContext()).thenReturn(executionContext); // 执行方法 ExitStatus result = myTasklet.afterStep(stepExecution); // 验证结果 assertEquals(ExitStatus.COMPLETED, result); // 验证ExecutionContext是否放入了指定键值对 verify(executionContext).put("outputs", "results"); } }
关键测试要点
- 模拟Spring Batch核心对象:用Mockito模拟
StepExecution、JobExecution等,避免依赖真实的Spring Batch运行环境。 - 私有变量/方法的处理:
- 私有变量
inputFiles可以通过反射设置或读取,用于验证beforeStep的赋值逻辑。 - 私有方法
parseFile可以通过Mockito的spy对象监控调用情况,或模拟异常场景。
- 私有变量
- 分方法独立测试:分别验证
beforeStep、execute、afterStep的逻辑,确保每个方法的职责都被覆盖。 - 异常场景验证:测试
parseFile抛出异常时,execute是否正确抛出RuntimeException。
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

