Rewrite JobLauncherApplicationRunnerTests
This commit rewrites the test to use an in-memory database rather than the deprecated Map-based arrangement. Closes gh-23369
This commit is contained in:
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 final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class))
|
||||
.withUserConfiguration(BatchConfiguration.class)
|
||||
.withPropertyValues("spring.datasource.initialization-mode=never");
|
||||
|
||||
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() {
|
||||
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 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);
|
||||
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 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 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 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 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 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 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 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");
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@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 {
|
|||
};
|
||||
}
|
||||
|
||||
static class JobLauncherApplicationRunnerContext {
|
||||
|
||||
private final JobLauncherApplicationRunner runner;
|
||||
|
||||
private final JobExplorer jobExplorer;
|
||||
|
||||
private final JobBuilderFactory jobs;
|
||||
|
||||
private final StepBuilderFactory steps;
|
||||
|
||||
private final Job job;
|
||||
|
||||
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 executeJob(JobParameters jobParameters) throws JobExecutionException {
|
||||
this.runner.execute(this.job, jobParameters);
|
||||
}
|
||||
|
||||
JobBuilder jobBuilder() {
|
||||
return this.jobs.get("job");
|
||||
}
|
||||
|
||||
StepBuilder stepBuilder() {
|
||||
return this.steps.get("step");
|
||||
}
|
||||
|
||||
SimpleJobBuilder configureJob() {
|
||||
return this.jobs.get("job").start(this.step);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableBatchProcessing
|
||||
@SuppressWarnings("deprecation")
|
||||
static class BatchConfiguration implements BatchConfigurer {
|
||||
static class BatchConfiguration extends BasicBatchConfigurer {
|
||||
|
||||
private ResourcelessTransactionManager transactionManager = new ResourcelessTransactionManager();
|
||||
private final DataSource dataSource;
|
||||
|
||||
private JobRepository jobRepository;
|
||||
|
||||
private org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean jobRepositoryFactory = new org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean(
|
||||
this.transactionManager);
|
||||
|
||||
BatchConfiguration() throws Exception {
|
||||
this.jobRepository = this.jobRepositoryFactory.getObject();
|
||||
protected BatchConfiguration(DataSource dataSource) {
|
||||
super(new BatchProperties(), dataSource, new TransactionManagerCustomizers(null));
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
this.jobRepositoryFactory.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobRepository getJobRepository() {
|
||||
return this.jobRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformTransactionManager getTransactionManager() {
|
||||
return this.transactionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobLauncher getJobLauncher() {
|
||||
SimpleJobLauncher launcher = new SimpleJobLauncher();
|
||||
launcher.setJobRepository(this.jobRepository);
|
||||
launcher.setTaskExecutor(new SyncTaskExecutor());
|
||||
return launcher;
|
||||
}
|
||||
|
||||
@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…
Reference in New Issue