不使用@Autowired注入JobLauncherTestUtils进行Spring Batch单元测试遇空指针
问题分析与解决方案
你的核心问题是直接Mockito模拟了JobLauncherTestUtils,而Mockito默认会让所有mock对象的方法返回null,所以launchJob()调用自然返回null,触发空指针异常。而且当前测试逻辑没有关联到MyjobConfiguration的实际代码,完全偏离了测试目标。
下面分两种场景给出修正方案:
场景1:测试myJob Bean的构建逻辑
如果你的目标是验证MyjobConfiguration中myJob方法是否正确构建了Job(比如设置正确的作业名、步骤、监听器等),不需要用到JobLauncherTestUtils,直接mock依赖并调用方法验证即可:
class MyjobConfigurationTest { // 实例化配置类,不要mock它(我们要测它的方法逻辑) private final MyjobConfiguration myjobConfiguration = new MyjobConfiguration(); // mock配置类的依赖 private final Step convertStep = Mockito.mock(Step.class); private final Step writeStep = Mockito.mock(Step.class); private final JobBuilderFactory jobBuilderFactory = Mockito.mock(JobBuilderFactory.class); private final JobListener jobListener = Mockito.mock(JobListener.class); @Test void myJob_ShouldBuildWithCorrectConfig() { // 模拟JobBuilderFactory的链式调用行为 JobBuilder jobBuilder = Mockito.mock(JobBuilder.class); SimpleJobBuilder simpleJobBuilder = Mockito.mock(SimpleJobBuilder.class); Job mockJob = Mockito.mock(Job.class); JobInstance mockJobInstance = Mockito.mock(JobInstance.class); // 预设mock的返回值 Mockito.when(jobBuilderFactory.get("myJob")).thenReturn(jobBuilder); Mockito.when(jobBuilder.incrementer(Mockito.any(RunIdIncrementer.class))).thenReturn(jobBuilder); Mockito.when(jobBuilder.start(convertStep)).thenReturn(simpleJobBuilder); Mockito.when(simpleJobBuilder.next(writeStep)).thenReturn(simpleJobBuilder); Mockito.when(simpleJobBuilder.listener(jobListener)).thenReturn(simpleJobBuilder); Mockito.when(simpleJobBuilder.build()).thenReturn(mockJob); Mockito.when(mockJob.getJobInstance()).thenReturn(mockJobInstance); Mockito.when(mockJobInstance.getJobName()).thenReturn("myJob"); // 测试带监听器的情况 Job jobWithListener = myjobConfiguration.myJob(convertStep, writeStep, Optional.of(jobListener), jobBuilderFactory); org.assertj.core.api.Assertions.assertThat(jobWithListener.getJobInstance().getJobName()).isEqualTo("myJob"); // 测试不带监听器的情况 Job jobWithoutListener = myjobConfiguration.myJob(convertStep, writeStep, Optional.empty(), jobBuilderFactory); org.assertj.core.api.Assertions.assertThat(jobWithoutListener.getJobInstance().getJobName()).isEqualTo("myJob"); } }
场景2:模拟作业执行流程
如果需要测试作业执行的结果验证逻辑,不能mockJobLauncherTestUtils,而是要手动配置它,给它传入mock的Job和JobLauncher来模拟执行结果:
class MyjobConfigurationTest { private final MyjobConfiguration myjobConfiguration = new MyjobConfiguration(); private final Step convertStep = Mockito.mock(Step.class); private final Step writeStep = Mockito.mock(Step.class); private final JobBuilderFactory jobBuilderFactory = Mockito.mock(JobBuilderFactory.class); private final JobLauncher jobLauncher = Mockito.mock(JobLauncher.class); private final JobLauncherTestUtils jobLauncherTestUtils = new JobLauncherTestUtils(); @org.junit.jupiter.api.BeforeEach void setUp() throws Exception { // 构建mock的Job对象 Job mockJob = Mockito.mock(Job.class); JobExecution mockJobExecution = Mockito.mock(JobExecution.class); JobInstance mockJobInstance = Mockito.mock(JobInstance.class); ExitStatus successExitStatus = new ExitStatus("COMPLETED"); // 预设Job构建逻辑的返回值 Mockito.when(jobBuilderFactory.get("myJob")).thenReturn(Mockito.mock(JobBuilder.class)); Mockito.when(myjobConfiguration.myJob(convertStep, writeStep, Optional.empty(), jobBuilderFactory)).thenReturn(mockJob); // 模拟JobLauncher执行成功的场景 Mockito.when(jobLauncher.run(Mockito.eq(mockJob), Mockito.any(JobParameters.class))).thenReturn(mockJobExecution); Mockito.when(mockJobExecution.getJobInstance()).thenReturn(mockJobInstance); Mockito.when(mockJobInstance.getJobName()).thenReturn("myJob"); Mockito.when(mockJobExecution.getExitStatus()).thenReturn(successExitStatus); // 配置JobLauncherTestUtils jobLauncherTestUtils.setJob(mockJob); jobLauncherTestUtils.setJobLauncher(jobLauncher); } @org.junit.jupiter.api.Test void whenJobExecuted_thenSuccess() throws Exception { JobExecution jobExecution = jobLauncherTestUtils.launchJob( new JobParametersBuilder().addString("timestamp", java.time.LocalDateTime.now().toString()) .toJobParameters()); org.assertj.core.api.Assertions.assertThat(jobExecution.getJobInstance().getJobName()).isEqualTo("myJob"); org.assertj.core.api.Assertions.assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo("COMPLETED"); } }
关键注意点
- 不要mock测试工具类:
JobLauncherTestUtils是用来辅助测试的工具,需要给它提供真实或mock的依赖(Job、JobLauncher)才能正常工作,直接mock它没有意义。 - 区分测试目标:如果是测Bean构建,就直接调用配置类的方法验证;如果是测执行逻辑,就模拟JobLauncher的返回结果,配置到测试工具中。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

