Skip to content

JobScopeTestExecutionListener invokes methods with JobExecution return type #3976

Open
@marschall

Description

@marschall

Bug description
JobScopeTestExecutionListener invokes any method with the JobExecution return type. Consider the following test with uses a utility method to launch a job. The method will be invoked by JobScopeTestExecutionListener before the @BeforeEach and @Test method. In this case causing the test to fail because the jobParameters is null.

class MyBatchTests {

  private JobParameters jobParameters;

  @BeforeEach
  void setUp() {
    this.jobParameters = ...;
  }

  private JobExecution launchJobUtilityMethod() throws Exception {
    return this.jobLauncherTestUtils.launchJob(this.jobParameters);
  }

  @Test
  void batchTest() throws Exception {
    JobExecution jobExecution = this.launchJobUtilityMethod();
    assert...;
  }

}

Environment
Spring Batch version: 4.3.3
Java version: 11.0.12
Database: H2 1.4.200

Steps to reproduce

  • Create a test with a JobScopeTestExecutionListener, for example by adding @SpringBatchTest
  • Add a method with return type JobExecution, can have any number of arguments

Expected behavior
org.springframework.batch.test.JobScopeTestExecutionListener#getJobExecution(TestContext) looks only at fields as the comment says.

Minimal Complete Reproducible example

@SpringBatchTest
class JobScopeTestExecutionListenerTests {

  private JobExecution shouldNeverBeInvoked(String s, int i) {
    throw new IllegalStateException("should never be invoked");
  }

  @Test
  void testMethod() throws Exception {
  }

  @Configuration
  @EnableBatchProcessing
  static class ContextConfiguration {

    private static final Log LOGGER = LogFactory.getLog(MethodHandles.lookup().lookupClass());

    @Autowired
    public JobBuilderFactory jobBuilderFactory;

    @Autowired
    public StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job loggingJob() {
      return this.jobBuilderFactory.get("loggingJob")
        .incrementer(new RunIdIncrementer())
        .start(this.step1())
        .build();
    }

    @Bean
    public Step step1() {
      return this.stepBuilderFactory.get("step1")
        .tasklet(this.loggingTasklet("step1"))
        .build();
    }

    @Bean
    public DataSource dataSource() {
      return new EmbeddedDatabaseBuilder()
              .generateUniqueName(true)
              .setType(H2)
              .build();
    }

    private Tasklet loggingTasklet(String stepName) {
      CallableTaskletAdapter taskletAdapter = new CallableTaskletAdapter();
      taskletAdapter.setCallable(() -> {
        LOGGER.info("executing step: " + stepName);
        return RepeatStatus.FINISHED;
      });
      return taskletAdapter;
    }

  }

}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions