Rewrite JobLauncherApplicationRunnerTests

This commit rewrites the test to use an in-memory database rather than
the deprecated Map-based arrangement.

Closes gh-23369
pull/23465/head
Stephane Nicoll 4 years ago
parent 17d5e17069
commit 4d10fbfd52

@ -16,31 +16,39 @@
package org.springframework.boot.autoconfigure.batch;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import java.util.List;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.job.builder.SimpleJobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.core.io.ResourceLoader;
import org.springframework.transaction.PlatformTransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
@ -57,138 +65,101 @@ import static org.assertj.core.api.Assertions.fail;
*/
class JobLauncherApplicationRunnerTests {
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
private JobLauncherApplicationRunner runner;
private JobExplorer jobExplorer;
private JobBuilderFactory jobs;
private StepBuilderFactory steps;
private Job job;
private Step step;
@BeforeEach
void init() {
this.context.register(BatchConfiguration.class);
this.context.refresh();
JobRepository jobRepository = this.context.getBean(JobRepository.class);
JobLauncher jobLauncher = this.context.getBean(JobLauncher.class);
this.jobs = new JobBuilderFactory(jobRepository);
PlatformTransactionManager transactionManager = this.context.getBean(PlatformTransactionManager.class);
this.steps = new StepBuilderFactory(jobRepository, transactionManager);
Tasklet tasklet = (contribution, chunkContext) -> null;
this.step = this.steps.get("step").tasklet(tasklet).build();
this.job = this.jobs.get("job").start(this.step).build();
this.jobExplorer = this.context.getBean(JobExplorer.class);
this.runner = new JobLauncherApplicationRunner(jobLauncher, this.jobExplorer, jobRepository);
this.context.getBean(BatchConfiguration.class).clear();
}
@AfterEach
void closeContext() {
this.context.close();
}
@Test
void basicExecution() throws Exception {
this.runner.execute(this.job, new JobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
this.runner.execute(this.job, new JobParametersBuilder().addLong("id", 1L).toJobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(2);
}
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class))
.withUserConfiguration(BatchConfiguration.class)
.withPropertyValues("spring.datasource.initialization-mode=never");
@Test
void incrementExistingExecution() throws Exception {
this.job = this.jobs.get("job").start(this.step).incrementer(new RunIdIncrementer()).build();
this.runner.execute(this.job, new JobParameters());
this.runner.execute(this.job, new JobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(2);
void basicExecution() {
this.contextRunner.run((context) -> {
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
jobLauncherContext.executeJob(new JobParameters());
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
jobLauncherContext.executeJob(new JobParametersBuilder().addLong("id", 1L).toJobParameters());
assertThat(jobLauncherContext.jobInstances()).hasSize(2);
});
}
@Test
void retryFailedExecution() throws Exception {
this.job = this.jobs.get("job").start(this.steps.get("step").tasklet(throwingTasklet()).build())
.incrementer(new RunIdIncrementer()).build();
this.runner.execute(this.job, new JobParameters());
this.runner.execute(this.job, new JobParametersBuilder().addLong("run.id", 1L).toJobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
void incrementExistingExecution() {
this.contextRunner.run((context) -> {
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
Job job = jobLauncherContext.configureJob().incrementer(new RunIdIncrementer()).build();
jobLauncherContext.runner.execute(job, new JobParameters());
jobLauncherContext.runner.execute(job, new JobParameters());
assertThat(jobLauncherContext.jobInstances()).hasSize(2);
});
}
@Test
void runDifferentInstances() throws Exception {
this.job = this.jobs.get("job").start(this.steps.get("step").tasklet(throwingTasklet()).build()).build();
// start a job instance
JobParameters jobParameters = new JobParametersBuilder().addString("name", "foo").toJobParameters();
this.runner.execute(this.job, jobParameters);
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
// start a different job instance
JobParameters otherJobParameters = new JobParametersBuilder().addString("name", "bar").toJobParameters();
this.runner.execute(this.job, otherJobParameters);
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(2);
void retryFailedExecution() {
this.contextRunner.run((context) -> {
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
Job job = jobLauncherContext.jobBuilder()
.start(jobLauncherContext.stepBuilder().tasklet(throwingTasklet()).build())
.incrementer(new RunIdIncrementer()).build();
jobLauncherContext.runner.execute(job, new JobParameters());
jobLauncherContext.runner.execute(job, new JobParametersBuilder().addLong("run.id", 1L).toJobParameters());
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
});
}
@Test
void retryFailedExecutionOnNonRestartableJob() throws Exception {
this.job = this.jobs.get("job").preventRestart()
.start(this.steps.get("step").tasklet(throwingTasklet()).build()).incrementer(new RunIdIncrementer())
.build();
this.runner.execute(this.job, new JobParameters());
this.runner.execute(this.job, new JobParameters());
// A failed job that is not restartable does not re-use the job params of
// the last execution, but creates a new job instance when running it again.
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(2);
assertThatExceptionOfType(JobRestartException.class).isThrownBy(() -> {
// try to re-run a failed execution
this.runner.execute(this.job, new JobParametersBuilder().addLong("run.id", 1L).toJobParameters());
fail("expected JobRestartException");
}).withMessageContaining("JobInstance already exists and is not restartable");
void runDifferentInstances() {
this.contextRunner.run((context) -> {
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
Job job = jobLauncherContext.jobBuilder()
.start(jobLauncherContext.stepBuilder().tasklet(throwingTasklet()).build()).build();
// start a job instance
JobParameters jobParameters = new JobParametersBuilder().addString("name", "foo").toJobParameters();
jobLauncherContext.runner.execute(job, jobParameters);
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
// start a different job instance
JobParameters otherJobParameters = new JobParametersBuilder().addString("name", "bar").toJobParameters();
jobLauncherContext.runner.execute(job, otherJobParameters);
assertThat(jobLauncherContext.jobInstances()).hasSize(2);
});
}
@Test
void retryFailedExecutionWithNonIdentifyingParameters() throws Exception {
this.job = this.jobs.get("job").start(this.steps.get("step").tasklet(throwingTasklet()).build())
.incrementer(new RunIdIncrementer()).build();
JobParameters jobParameters = new JobParametersBuilder().addLong("id", 1L, false).addLong("foo", 2L, false)
.toJobParameters();
this.runner.execute(this.job, jobParameters);
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
// try to re-run a failed execution with non identifying parameters
this.runner.execute(this.job, new JobParametersBuilder(jobParameters).addLong("run.id", 1L).toJobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
void retryFailedExecutionOnNonRestartableJob() {
this.contextRunner.run((context) -> {
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
Job job = jobLauncherContext.jobBuilder().preventRestart()
.start(jobLauncherContext.stepBuilder().tasklet(throwingTasklet()).build())
.incrementer(new RunIdIncrementer()).build();
jobLauncherContext.runner.execute(job, new JobParameters());
jobLauncherContext.runner.execute(job, new JobParameters());
// A failed job that is not restartable does not re-use the job params of
// the last execution, but creates a new job instance when running it again.
assertThat(jobLauncherContext.jobInstances()).hasSize(2);
assertThatExceptionOfType(JobRestartException.class).isThrownBy(() -> {
// try to re-run a failed execution
jobLauncherContext.runner.execute(job,
new JobParametersBuilder().addLong("run.id", 1L).toJobParameters());
fail("expected JobRestartException");
}).withMessageContaining("JobInstance already exists and is not restartable");
});
}
@Test
void retryFailedExecutionWithDifferentNonIdentifyingParametersFromPreviousExecution() throws Exception {
this.job = this.jobs.get("job").start(this.steps.get("step").tasklet(throwingTasklet()).build())
.incrementer(new RunIdIncrementer()).build();
JobParameters jobParameters = new JobParametersBuilder().addLong("id", 1L, false).addLong("foo", 2L, false)
.toJobParameters();
this.runner.execute(this.job, jobParameters);
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
// try to re-run a failed execution with non identifying parameters
this.runner.execute(this.job, new JobParametersBuilder().addLong("run.id", 1L).addLong("id", 2L, false)
.addLong("foo", 3L, false).toJobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
JobInstance jobInstance = this.jobExplorer.getJobInstance(0L);
assertThat(this.jobExplorer.getJobExecutions(jobInstance)).hasSize(2);
// first execution
JobExecution firstJobExecution = this.jobExplorer.getJobExecution(0L);
JobParameters parameters = firstJobExecution.getJobParameters();
assertThat(parameters.getLong("run.id")).isEqualTo(1L);
assertThat(parameters.getLong("id")).isEqualTo(1L);
assertThat(parameters.getLong("foo")).isEqualTo(2L);
// second execution
JobExecution secondJobExecution = this.jobExplorer.getJobExecution(1L);
parameters = secondJobExecution.getJobParameters();
// identifying parameters should be the same as previous execution
assertThat(parameters.getLong("run.id")).isEqualTo(1L);
// non-identifying parameters should be the newly specified ones
assertThat(parameters.getLong("id")).isEqualTo(2L);
assertThat(parameters.getLong("foo")).isEqualTo(3L);
void retryFailedExecutionWithNonIdentifyingParameters() {
this.contextRunner.run((context) -> {
JobLauncherApplicationRunnerContext jobLauncherContext = new JobLauncherApplicationRunnerContext(context);
Job job = jobLauncherContext.jobBuilder()
.start(jobLauncherContext.stepBuilder().tasklet(throwingTasklet()).build())
.incrementer(new RunIdIncrementer()).build();
JobParameters jobParameters = new JobParametersBuilder().addLong("id", 1L, false).addLong("foo", 2L, false)
.toJobParameters();
jobLauncherContext.runner.execute(job, jobParameters);
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
// try to re-run a failed execution with non identifying parameters
jobLauncherContext.runner.execute(job,
new JobParametersBuilder(jobParameters).addLong("run.id", 1L).toJobParameters());
assertThat(jobLauncherContext.jobInstances()).hasSize(1);
});
}
private Tasklet throwingTasklet() {
@ -197,48 +168,67 @@ class JobLauncherApplicationRunnerTests {
};
}
@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
@SuppressWarnings("deprecation")
static class BatchConfiguration implements BatchConfigurer {
static class JobLauncherApplicationRunnerContext {
private final JobLauncherApplicationRunner runner;
private final JobExplorer jobExplorer;
private ResourcelessTransactionManager transactionManager = new ResourcelessTransactionManager();
private final JobBuilderFactory jobs;
private JobRepository jobRepository;
private final StepBuilderFactory steps;
private org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean jobRepositoryFactory = new org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean(
this.transactionManager);
private final Job job;
BatchConfiguration() throws Exception {
this.jobRepository = this.jobRepositoryFactory.getObject();
private final Step step;
JobLauncherApplicationRunnerContext(ApplicationContext context) {
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
JobRepository jobRepository = context.getBean(JobRepository.class);
this.jobs = new JobBuilderFactory(jobRepository);
this.steps = new StepBuilderFactory(jobRepository, context.getBean(PlatformTransactionManager.class));
this.step = this.steps.get("step").tasklet((contribution, chunkContext) -> null).build();
this.job = this.jobs.get("job").start(this.step).build();
this.jobExplorer = context.getBean(JobExplorer.class);
this.runner = new JobLauncherApplicationRunner(jobLauncher, this.jobExplorer, jobRepository);
}
List<JobInstance> jobInstances() {
return this.jobExplorer.getJobInstances("job", 0, 100);
}
void clear() {
this.jobRepositoryFactory.clear();
void executeJob(JobParameters jobParameters) throws JobExecutionException {
this.runner.execute(this.job, jobParameters);
}
@Override
public JobRepository getJobRepository() {
return this.jobRepository;
JobBuilder jobBuilder() {
return this.jobs.get("job");
}
@Override
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
StepBuilder stepBuilder() {
return this.steps.get("step");
}
@Override
public JobLauncher getJobLauncher() {
SimpleJobLauncher launcher = new SimpleJobLauncher();
launcher.setJobRepository(this.jobRepository);
launcher.setTaskExecutor(new SyncTaskExecutor());
return launcher;
SimpleJobBuilder configureJob() {
return this.jobs.get("job").start(this.step);
}
}
@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
static class BatchConfiguration extends BasicBatchConfigurer {
private final DataSource dataSource;
protected BatchConfiguration(DataSource dataSource) {
super(new BatchProperties(), dataSource, new TransactionManagerCustomizers(null));
this.dataSource = dataSource;
}
@Override
public JobExplorer getJobExplorer() throws Exception {
return new org.springframework.batch.core.explore.support.MapJobExplorerFactoryBean(
this.jobRepositoryFactory).getObject();
@Bean
BatchDataSourceInitializer batchDataSourceInitializer(ResourceLoader resourceLoader) {
return new BatchDataSourceInitializer(this.dataSource, resourceLoader, new BatchProperties());
}
}

Loading…
Cancel
Save