由于线程执行,spring批处理的java集成测试失败

wn9m85ua  于 2021-07-15  发布在  Java
关注(0)|答案(1)|浏览(425)

我有一个spring批处理项目。目标是对spring作业中存在的各个步骤执行集成测试。
我正在使用joblaunchertestutils启动步骤。但是,当这个util启动这些步骤时,它会在一个单独的线程中运行它。一旦线程完成执行,就应该为其分配一些值 jobExecution.getStepExecutions() .
问题:由于某种原因,即使在线程完成执行之前,测试也会转到下一行 List<StepExecution> actualStepExecutions = new ArrayList<>(jobExecution.getStepExecutions()) ,在哪里 jobExecution.getStepExecutions() 当前为空,因此测试失败 NullPointerException 以及 Index 0 out of bounds for length 0 -标记在下面的测试类中。
问题:有没有合适的方法来等待步骤执行线程完成,然后转到测试中的下一行进行验证?
代码:
集成测试类:

@Slf4j
@SpringBatchTest
@SpringBootTest
@ActiveProfiles({"test", "master"})
@ContextConfiguration(classes = {InhouseClass3.class, InhouseClass1.class, InhouseClass2.class})
public class BatchJobIntegrationTest {

    private static final String Param1 = "someParam";

    @Autowired
    @Qualifier("hikariDatasource")
    DataSource hikariDatasource;

    @Autowired
    Job BatchJob;

    @Autowired
    JobLauncher jobLauncher;

    JobExecution jobExecution;

    @Autowired
    CreateDirectoryTasklet createDirectoryTasklet;

    JobParameters jobParameters;

    JobLauncherTestUtils jobLauncherTestUtils;

    @BeforeEach
    void setUp() {
        String startTimestamp = Timestamp.from(Instant.now()).toString();
        jobParameters = new JobParametersBuilder()
                .addString(Param1, startTimestamp)
                .toJobParameters();

        jobLauncherTestUtils = new JobLauncherTestUtils();
        jobLauncherTestUtils.setJob(BatchJob);
        jobLauncherTestUtils.setJobLauncher(jobLauncher);
    }

    @SneakyThrows
    @Test
    void TaskletToTest_Test() {
        jobExecution = jobLauncherTestUtils.launchStep("loadTaskletToTest", jobParameters);

        List<StepExecution> actualStepExecutions = new ArrayList<>(jobExecution.getStepExecutions());
        // ERROR: jobExecution.getStepExecutions() is NULL. 

        ExitStatus actualJobExitStatus = actualStepExecutions.get(0).getExitStatus();
        // ERROR: Index 0 out of bounds for length 0

        assertEquals("loadGdxClaims", actualStepExecutions.get(0).getStepName());
        assertEquals(ExitStatus.COMPLETED, actualJobExitStatus);
    }

    @SneakyThrows
    @Test
    // This is my workaround to make my above test run. 
    // I added a sleep for 2 seconds. But this doesn't look like an ideal way, coz what 
    // if the launchstep thread running the tasklet took more than 2 seconds? 
    void loadGDXClaimTaskletTest_Working_() {
        jobExecution = jobLauncherTestUtils.launchStep("createDirectory", jobParameters);
        boolean counter = true;
        while(counter) {
            if (jobExecution.getStepExecutions().size()!=0 ) {
                List<StepExecution> actualStepExecutions = new ArrayList<>(jobExecution.getStepExecutions());

                ExitStatus actualJobExitStatus = actualStepExecutions.get(0).getExitStatus();
                log.info("------- step executions : {}", actualStepExecutions);
                assertEquals("createDirectory", actualStepExecutions.get(0).getStepName());
                assertEquals(ExitStatus.COMPLETED, actualJobExitStatus);
                counter = false;
            } else {
                TimeUnit.SECONDS.sleep(2);
            }
        }
    }

}

要测试的tasklet步骤:

@Slf4j
@Component
public class TaskletToTest implements Tasklet {

    private final InhouseService inhouseService;

    public TaskletToTest(InhouseService inhouseService) {
        this.inhouseService = inhouseService;
    }

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws InterruptedException, IllegalJobNameException, JSchException, IOException {
        log.info("TaskletToTest before");
        inhouseService.retry();
        log.info("TaskletToTest after");
        return RepeatStatus.FINISHED;
    }
}

包含需要测试的步骤列表的批处理作业:

@Slf4j
@Profile("master")
@Configuration
public class MasterConfig extends DefaultBatchConfigurer {
    @Bean(name = "BatchJob")
    public Job remoteChunkingJob(TaskletStep someOtherTasklet,
                                 TaskletStep loadTaskletToTest,
                                 JobExecutionListener jobExecutionListener) {
        return this.jobBuilderFactory.get("extract gdx load")
                .incrementer(new RunIdIncrementer())     
                .listener(jobExecutionListener)
                .start(someOtherTasklet) 
                .next(loadTaskletToTest)
                .build();
    }

    @Bean
    TaskletStep loadTaskletToTest(TaskletToTest taskletToTest) {
        return this.stepBuilderFactory.get("loadTaskletToTest").tasklet(taskletToTest).build();
    }
}
yrefmtwq

yrefmtwq1#

我正在使用joblaunchertestutils启动步骤。但是,当这个util启动这些步骤时,它会在一个单独的线程中运行它。
问题:由于某种原因,即使在线程完成执行之前,测试也会转到下一行 JobLauncherTestUtils 使用 JobLauncher 启动作业和步骤。所以取决于 JobLauncher 在您使用的实现中,可以在当前线程或单独的线程中运行作业/步骤。
你没有分享哪个 JobLauncher 在测试中是自动连接的,但是您似乎已经定义了一个基于异步 TaskExecutor 实施。这就是为什么在后台执行作业/步骤的原因:

// This returns immediately with an asynchrnous TaskExecutor
// However, with a synchronous TaskExectuor it will block waiting for the step to finish 
jobExecution = jobLauncherTestUtils.launchStep("loadTaskletToTest", jobParameters);

List<StepExecution> actualStepExecutions = new ArrayList<>(jobExecution.getStepExecutions());

所以你需要检查一下 JobLauncher (通常为 SimpleJobLauncher 使用同步或异步 TaskExecutor )在你的测试中是自动连线的。

相关问题