You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用@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");
    }
}

关键注意点

  1. 不要mock测试工具类:JobLauncherTestUtils是用来辅助测试的工具,需要给它提供真实或mock的依赖(Job、JobLauncher)才能正常工作,直接mock它没有意义。
  2. 区分测试目标:如果是测Bean构建,就直接调用配置类的方法验证;如果是测执行逻辑,就模拟JobLauncher的返回结果,配置到测试工具中。

内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 00:42:49