【问题标题】:Spring Batch JUnit test for multiple jobs多个作业的 Spring Batch JUnit 测试
【发布时间】:2016-03-16 23:40:23
【问题描述】:

我在一个上下文文件中配置了两个作业

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

我尝试使用@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]`

【问题讨论】:

  • 添加您的测试代码和完整的堆栈跟踪。

标签: spring spring-batch


【解决方案1】:

您在 bean 配置文件中声明了两个相似的 bean。 要解决上述问题,您需要 @Qualifier("JobA")@Qualifier("JobB") 告诉 Spring 哪个 bean 应该自动连接到哪个作业。

【讨论】:

  • Job Bean 在 JobLauncherTestUtils 中自动装配,预计每批单个 bean。
【解决方案2】:

也许迟到了,

但我为自己找到了可行的解决方案: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。 (默认情况下,它需要上下文中的单个作业配置)

【讨论】:

  • 对我来说,它仍然运行这两项工作。我已经按原样尝试了这个解决方案。我不知道还能做什么。
  • 为了他人的利益。使用这种方法删除 @SpringBatchTest 注释。提到的注解是自动注入JobLauncherTestUtils
【解决方案3】:

因为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】:

    我通过为每个作业分别创建 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】:

      不回答原始问题,但使用下面的代码,我们避免了在同一类的测试用例顺序运行期间重用 JobLauncherTestUtils。

      @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
      

      这表明 Junit 在每次运行后清理并重新构建上下文。

      【讨论】:

        【解决方案6】:

        我们遇到了同样的问题,因为我们在项目中也使用了spring-cloud-configuration,需要@SpringBootTest注解,然后加载整个Spring Boot上下文,所以在上下文中加载了多个作业。

        我们得出的解决方案接近于Ilya Dyoshin提供的解决方案,带有构造函数注入和Junit 5:

        @ExtendWith(SpringExtension.class)
        @SpringBootTest
        class MyJobConfigTest {
        
            private final JobLauncherTestUtils jobLauncherTestUtils;
        
            @Autowired
            public MyJobConfigTest(Job jobNumber1, JobLauncher jobLauncher, JobRepository jobRepository) {
                this.jobLauncherTestUtils = new JobLauncherTestUtils();
                this.jobLauncherTestUtils.setJobLauncher(jobLauncher);
                this.jobLauncherTestUtils.setJobRepository(jobRepository);
                this.jobLauncherTestUtils.setJob(jobNumber1);
            }
        

        【讨论】:

          【解决方案7】:

          当使用 Spring Boot 时,我建议使用 @EnableBatchProcessing(modular = true) 将这两个作业分隔到不同的上下文中

          然后可以通过仅使用@SpringBootTest(classes = { JobAConfiguration.class, ... }) 提供特定配置来单独测试作业

          https://github.com/desprez/springbatch-modular 有一个伟大而全面的示例,包括针对不同工作的独立测试类(不是我,感谢作者)。

          【讨论】:

            【解决方案8】:

            spring-batch github site 对此进行了讨论。 manumouton 的评论总结了对我有用的东西。基本上,如果您正在使用它,请从您的测试类中删除 @SpringBatchTest 注释,并且对于您要测试的每个 Job,在您的测试配置中添加一个 Bean 以提供一个单独的 JobLauncherTestUtils 实现,该实现覆盖 @Autowired setJob(Job job ) 方法来添加@Qualifier,以标识您要定位的特定工作。

            这和ilya-dyoshin的方案类似,但更能满足大家对IOC的执念。

            为了方便,我只是从github复制粘贴代码:

            @Configuration
            static class MyTestConfiguration {
            @Bean
            public JobLauncherTestUtils myJobLauncherTestUtils() {
              return new JobLauncherTestUtils() {
                @Override
                @Autowired
                public void setJob(@Qualifier("mySpecificJobQualifier") Job job) {
                  super.setJob(job);
                }
              };
            }
            

            }

            【讨论】:

              猜你喜欢
              • 2014-12-10
              • 2016-11-14
              • 2013-02-11
              • 2012-06-12
              • 1970-01-01
              • 2019-06-09
              • 2014-12-24
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多