问题
I am having two jobs configured in one context file
<batch:job id="JobA" restartable="true">
<batch:step id="abc">
<batch:tasklet >
<batch:chunk reader="reader" writer="writer" processor="processor" />
</batch:tasklet>
</batch:step>
</batch:job>
<batch:job id="JobB" restartable="true">
<batch:step id="abc">
<batch:tasklet >
<batch:chunk reader="reader" writer="writer" processor="processor" />
</batch:tasklet>
</batch:step>
</batch:job>
When i am doing unit testing for the JobA using JobLauncherTestUtils and testing the job launch it is throwing an exception saying
No unique bean of type [org.springframework.batch.core.Job;] is defined: expected single matching bean but found 2: [JobA, JobB]
i tried using @Qualifier for autowire still the same thing. Where am i doing wrong here
edited
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:META-INF/spring/batch-test-context.xml" })
public class TestJob {
@Autowired
private JobExplorer jobExplorer;
@Autowired
@Qualifier("JobA")
private Job JobA;
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@Test
public void testJob() throws Exception {
JobParameters jobParameters = getNextJobParameters(getJobParameters());
assertEquals(BatchStatus.COMPLETED, jobLauncherTestUtils.getJobLauncher().run(JobA, jobParameters));
}
private JobParameters getJobParameters() {
JobParametersBuilder jobParameters = new JobParametersBuilder();
jobParameters.addString("param", "123");
return jobParameters.toJobParameters();
}
private JobParameters getNextJobParameters(JobParameters jobParameters) {
String jobIdentifier = jobLauncherTestUtils.getJob().getName();
List<JobInstance> lastInstances = jobExplorer.getJobInstances(jobIdentifier, 0, 1);
JobParametersIncrementer incrementer = jobLauncherTestUtils.getJob().getJobParametersIncrementer();
if (lastInstances.isEmpty()) {
return incrementer.getNext(jobParameters);
} else {
List<JobExecution> lastExecutions = jobExplorer.getJobExecutions(lastInstances.get(0));
return incrementer.getNext(lastExecutions.get(0).getJobParameters());
}
}
}
exception was
No unique bean of type [org.springframework.batch.core.Job;] is defined: expected single matching bean but found 2: [JobA, JobB]`
回答1:
Maybe late,
but I found for myself working solution: manual configuration of JobLauncherTestUtils:
@Inject
@Qualifier(value = "Job1")
private Job job;
@Inject
private JobLauncher jobLauncher;
@Inject
private JobRepository jobRepository;
private JobLauncherTestUtils jobLauncherTestUtils;
private void initailizeJobLauncherTestUtils() {
this.jobLauncherTestUtils = new JobLauncherTestUtils();
this.jobLauncherTestUtils.setJobLauncher(jobLauncher);
this.jobLauncherTestUtils.setJobRepository(jobRepository);
this.jobLauncherTestUtils.setJob(job);
}
@Before
public void setUp() throws Exception {
this.initailizeJobLauncherTestUtils();
}
with this you can control for which Job should JobLauncherTestUtils be applied. (by default it expects single Job configuration in context)
回答2:
You have two similar beans declared in bean configuration file.
To fix above problem, you need @Qualifier("JobA") and @Qualifier("JobB") to tell Spring about which bean should auto wired to which job.
回答3:
Because there is an @Autowired annotation on the setter for JobLauncherTestUtils.setJob(Job job) I had to use a MergedBeanDefinitionPostProcessor to set the property after the bean was created:
@Configuration
public class TestBatchConfiguration implements MergedBeanDefinitionPostProcessor {
@Autowired
@Qualifier("JobA")
private Job job;
@Bean(name="jtestl")
public JobLauncherTestUtils jobLauncherTestUtils() {
JobLauncherTestUtils jobLauncherTestUtils = new JobLauncherTestUtils();
jobLauncherTestUtils.setJob(job);
return jobLauncherTestUtils;
}
/**
* https://stackoverflow.com/questions/22416140/autowire-setter-override-with-java-config
* This is needed to inject the correct job into JobLauncherTestUtils
*/
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if(beanName.equals("jtestl")) {
beanDefinition.getPropertyValues().add("job", getMyBeanFirstAImpl());
}
}
private Object getMyBeanFirstAImpl() {
return job;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
回答4:
I solved it by creating JobLauncherTestUtils for each job separately (groovy):
@TestConfiguration class BatchJobTestConfiguration {
@Autowired
@Qualifier('job1')
private Job job1
@Autowired
@Qualifier('job2')
private Job job2
@Autowired
JobRepository jobRepository;
@Bean
JobLauncher jobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(new SyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
@Bean(name = 'jobLauncherTestUtilsJob1')
JobLauncherTestUtils jobLauncherTestUtilsSyncEndUserJob() {
new JobLauncherNoAutowireTestUtil(
job: job1,
jobLauncher: jobLauncher()
)
}
@Bean(name = 'jobLauncherTestUtilsJob2')
JobLauncherTestUtils jobLauncherTestUtilsenewCaseJob() {
new JobLauncherNoAutowireTestUtil(
job: job2,
jobLauncher: jobLauncher()
)
}
Then add this into your test:
@ContextConfiguration(classes = [BatchJobTestConfiguration])
...
@Autowired
@Qualifier('jobLauncherTestUtilsJob1')
private JobLauncherTestUtils jobLauncherTestUtils
...
when:
def jobExecution = jobLauncherTestUtils.launchJob()
回答5:
Not answer to original problem, but using below code we have avoided reuse of JobLauncherTestUtils during sequential run of test cases in same class.
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
This indicates Junit to clean up and re construct context after each run.
来源:https://stackoverflow.com/questions/34217101/spring-batch-junit-test-for-multiple-jobs