복붙노트

[SPRING] 여러 작업에 대한 Spring Batch JUnit 테스트

SPRING

여러 작업에 대한 Spring Batch JUnit 테스트

하나의 컨텍스트 파일에 두 개의 작업이 구성되어 있습니다.

<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>

JobLauncherTestUtils를 사용하여 JobA에 대한 단위 테스트를 수행하고 작업 시작을 테스트 할 때 예외가 발생했습니다.

No unique bean of type [org.springframework.batch.core.Job;] is defined: expected single matching bean but found 2: [JobA, JobB]

나는 autowire에 @Qualifier를 사용해 보았는데 여전히 같은 것을 사용했다. 내가 여기서 잘못하고있는 곳

편집 한

@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());
        }
    }
}

예외는

No unique bean of type [org.springframework.batch.core.Job;] is defined: expected single matching bean but found 2: [JobA, JobB]`

해결법

  1. ==============================

    1.어쩌면 늦었 어.

    어쩌면 늦었 어.

    하지만 스스로 해결책을 찾았습니다 : 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();
    }
    

    JobLauncherTestUtils를 적용해야하는 Job을 제어 할 수 있습니다. (기본적으로 컨텍스트에서 단일 작업 구성이 필요함)

  2. ==============================

    2.빈 환경 설정 파일에 선언 된 두 개의 비슷한 빈을 가지고있다. 위의 문제를 해결하기 위해 @Qualifier ( "JobA")와 @Qualifier ( "JobB")가 필요하다.

    빈 환경 설정 파일에 선언 된 두 개의 비슷한 빈을 가지고있다. 위의 문제를 해결하기 위해 @Qualifier ( "JobA")와 @Qualifier ( "JobB")가 필요하다.

  3. ==============================

    3.JobLauncherTestUtils.setJob (Job job)에 대한 setter에 @Autowired 주석이 있기 때문에 Bean이 생성 된 후에 MergedBeanDefinitionPostProcessor를 사용하여 속성을 설정해야했습니다.

    JobLauncherTestUtils.setJob (Job job)에 대한 setter에 @Autowired 주석이 있기 때문에 Bean이 생성 된 후에 MergedBeanDefinitionPostProcessor를 사용하여 속성을 설정해야했습니다.

    @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. ==============================

    4.개별적으로 JobLauncherTestUtils를 작성하여 해결했습니다 (groovy).

    개별적으로 JobLauncherTestUtils를 작성하여 해결했습니다 (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()
        )
    }
    

    그런 다음 테스트에 추가하십시오.

    @ContextConfiguration(classes = [BatchJobTestConfiguration])
    ...
    @Autowired
    @Qualifier('jobLauncherTestUtilsJob1')
    private JobLauncherTestUtils jobLauncherTestUtils
    ...
    when:
    def jobExecution = jobLauncherTestUtils.launchJob()
    
  5. ==============================

    5.원래의 문제에 대한 대답은 아니지만, 아래 코드를 사용하여 동일한 클래스의 테스트 케이스를 순차적으로 실행하는 동안 JobLauncherTestUtils를 재사용하지 않았습니다.

    원래의 문제에 대한 대답은 아니지만, 아래 코드를 사용하여 동일한 클래스의 테스트 케이스를 순차적으로 실행하는 동안 JobLauncherTestUtils를 재사용하지 않았습니다.

    @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
    

    이는 Junit이 각 실행 후에 컨텍스트를 정리하고 다시 구성하도록 지시합니다.

  6. from https://stackoverflow.com/questions/34217101/spring-batch-junit-test-for-multiple-jobs by cc-by-sa and MIT license