Skip to content

Simplify batch test utilities configuration [BATCH-2718] #889

Closed
@spring-projects-issues

Description

@spring-projects-issues

Mahmoud Ben Hassine opened BATCH-2718 and commented

Currently, in order to autowire and use the JobLauncherTestUtils in a JUnit test, a bean of type JobLauncherTestUtils should be registered in the test context:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {JobConfiguration.class, JobTest.TestConfiguration.class})
public class JobTest {

	@Autowired
	private JobLauncherTestUtils jobLauncherTestUtils;

	@Test
	public void testJob() throws Exception {
		// given
		JobParameters uniqueJobParameters = jobLauncherTestUtils.getUniqueJobParameters();
		JobParameters jobParameters = new JobParametersBuilder(uniqueJobParameters)
				.toJobParameters();

		// when
		JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);

		// then
		Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
	}

	@Configuration
	public static class TestConfiguration {

		@Bean
		public JobLauncherTestUtils jobLauncherTestUtils() {
			return new JobLauncherTestUtils();
		}

	}
}

In this example, JobConfiguration contains the configuration of the job under test, but as we can see, there is an additional effort required to create a configuration class, define a bean of type JobLauncherTestUtils in it and then import it in the test context (see usage of JobRunnerConfiguration class and job-runner-context.xml in some tests of the current code base).

It would be great if this bean (and other test utility beans and listeners like the JobRepositoryTestUtils, StepScopeTestExecutionListener and JobScopeTestExecutionListener) can be autowired in the test in a declarative way. There are multiple ways to do it and here are a couple of options I want to suggest:

Option 1: Create a new annotation @SpringBatchTest

This annotation would import a configuration class that contains batch test utilities beans. This is the same idea as SpringIntegration test for integration. An example would be:

@RunWith(SpringRunner.class)
@SpringBatchTest
@ContextConfiguration(classes = {JobConfiguration.class})
public class JobTest {

	@Autowired
	private JobLauncherTestUtils jobLauncherTestUtils;

	@Test
	public void testJob() throws Exception {
		// given
		JobParameters uniqueJobParameters = jobLauncherTestUtils.getUniqueJobParameters();
		JobParameters jobParameters = new JobParametersBuilder(uniqueJobParameters)
				.toJobParameters();

		// when
		JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);

		// then
		Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
	}

}

This annotation would also declare the StepScopeTestExecutionListener and JobScopeTestExecutionListener in addition to utility beans.

Option 2: Create a custom JUnit runner

I think it is also possible to create a custom batch runner that autowires the JobLauncherTestUtils (and other utility beans and listeners) in the test context. Here is an example:

@RunWith(SpringBatchRunner.class)
@ContextConfiguration(classes = {JobConfiguration.class})
public class JobTest {

	@Autowired
	private JobLauncherTestUtils jobLauncherTestUtils;

	@Test
	public void testJob() throws Exception {
		// given
		JobParameters uniqueJobParameters = jobLauncherTestUtils.getUniqueJobParameters();
		JobParameters jobParameters = new JobParametersBuilder(uniqueJobParameters)
				.toJobParameters();

		// when
		JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);

		// then
		Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
	}

}

With this option, the only concern is how to support both Junit 4 and Junit 5. AFAIK, Junit 5 has a different extension model than JUnit 4.

Option 3: Create a custom test context bootstrapper

Like option 2, it is probably possible to create a custom TestContextBootstrapper that autowires the JobLauncherTestUtils (and other utility beans and listeners) in the test context. Something like:

@RunWith(SpringRunner.class)
@BootstrapWith(SpringBatchTestContextBootstrapper.class)
@ContextConfiguration(classes = {JobConfiguration.class})
public class JobTest {

	@Autowired
	private JobLauncherTestUtils jobLauncherTestUtils;

	@Test
	public void testJob() throws Exception {
		// given
		JobParameters uniqueJobParameters = jobLauncherTestUtils.getUniqueJobParameters();
		JobParameters jobParameters = new JobParametersBuilder(uniqueJobParameters)
				.toJobParameters();

		// when
		JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters);

		// then
		Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
	}

}

With all these options, as a batch developer, I can import and autowire batch test utilities in a declarative way without having to configure them explicitly. I don't know what is the best way to implement the feature, so please let me know if there is a better option to do it. What do you think?


Affects: 4.0.1

Referenced from: pull request #605

Backported to: 4.1.0.M1

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions