当前位置: 首页 > 知识库问答 >
问题:

为什么在尝试对整个Spring批处理作业进行单元测试时会出现此错误?没有“org”类型的合格bean。springframework。一批果心“作业”可用

扶开诚
2023-03-14

我正在开发一个Spring Batch应用程序。直到现在,我才能对服务方法之类的东西进行单元测试(就像在每个Spring Boot应用程序中所做的那样)。

现在,我正试图遵循本教程,以便从单元测试类中测试整个作业(基本上,我想执行一个执行作业的测试方法):https://www.baeldung.com/spring-batch-testing-job

这是我的JUnit测试类,在这种情况下工作正常,我可以正确地使用@SpringBootTest注释测试我的服务方法:

@SpringBootTest
@SpringBatchTest
class UpdateInfoBatchApplicationTests {
    
    @Autowired
    private NotaryService notaryService;
    
    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;
  
    @Autowired
    private JobRepositoryTestUtils jobRepositoryTestUtils;
  
    @After
    public void cleanUp() {
    jobRepositoryTestUtils.removeJobExecutions();
    }
    
    @Autowired
    @Qualifier("launcher")
    private JobLauncher jobLauncher;
    
    @Autowired
    @Qualifier("updateNotaryDistrictsJob")
    private Job updateNotaryDistrictsJob;
    
    @Autowired
    @Qualifier("updateNotaryListInfoJob")
    private Job updateNotaryListInfoJob;
    
    private JobParameters defaultJobParameters() {
    JobParametersBuilder paramsBuilder = new JobParametersBuilder();
    //paramsBuilder.addString("file.input", TEST_INPUT);
    //paramsBuilder.addString("file.output", TEST_OUTPUT);
    return paramsBuilder.toJobParameters();
   }
    
    @Test
    public void givenReferenceOutput_whenJobExecuted_thenSuccess() throws Exception {
    // when
    JobExecution jobExecution = jobLauncherTestUtils.launchJob(defaultJobParameters());
    JobInstance actualJobInstance = jobExecution.getJobInstance();
    ExitStatus actualJobExitStatus = jobExecution.getExitStatus();
    
    Assert.assertEquals(actualJobInstance.getJobName(), "updateNotaryDistrictsJob");
      
    // then
    //assertThat(actualJobInstance.getJobName(), is("updateNotaryDistrictsJob"));
    //assertThat(actualJobExitStatus.getExitCode(), is("COMPLETED"));
    //AssertFile.assertFileEquals(expectedResult, actualResult);
    }

    @Test
    void contextLoads() {
        System.out.println("TEST - contextLoads()");
    }
    
    @Test
    void getNotaryList() throws Exception {
    List<Notary> notaryList = this.notaryService.getNotaryList();
    System.out.println("notaryList size: " + notaryList);
    Assert.assertEquals("Notary List must be 5069", 5069, notaryList.size());
    }
    
    @Test
    void getNotaryDetails() throws Exception {
        NotaryDetails notaryDetails = this.notaryService.getNotaryDetails("089cy5Ra9zE%253D");
        System.out.println("notaryDetails: " + notaryDetails);
        Assert.assertEquals("Notary ID must be 089cy5Ra9zE%253D", "089cy5Ra9zE%253D", notaryDetails.getIdNotary());
    }
    
    @Test
    void getNotaryDistrictsList() throws Exception {
        List<NotaryDistrict> notaryDistrictsList = this.notaryService.getNotaryDistrictsList();
        System.out.println("notaryDistrictsList: " + notaryDistrictsList);
        
        Assert.assertEquals("Notary districts list lenght must be 91", 91, notaryDistrictsList.size());
        
        //ArrayList<NotaryDistrict>  notaryDistrictsListArrayList = new ArrayList<NotaryDistrict>(notaryDistrictsList);
        notaryDistrictsList.remove(0);
        Assert.assertEquals("Notary districts list lenght must now be 90", 90, notaryDistrictsList.size());
    }
    
    @Test
    void getNotaryDistrictDetails() throws Exception {
        NotaryDistrictDetails notaryDistrictDetails =  this.notaryService.getNotaryDistrictDetails("CG7drXn9fvA%253D");
        System.out.println("notaryDistrictDetails: " + notaryDistrictDetails.toString());
        
        Assert.assertEquals("Distretto must be: SCIACCA", "SCIACCA", notaryDistrictDetails.getDistretto());
    }
}

正如您在前面的代码中看到的,我首先注入我定义的两个作业对象:

@Autowired
@Qualifier("launcher")
private JobLauncher jobLauncher;

@Autowired
@Qualifier("updateNotaryDistrictsJob")
private Job updateNotaryDistrictsJob;

这些作业被定义为bean,进入配置Spring批处理作业和步骤的类,基本上我有以下两个bean:

@Bean("updateNotaryDistrictsJob")
public Job updateNotaryDistrictsListInfoJob(){
    return jobs.get("updateNotaryDistrictsListInfoJob")
            .incrementer(new RunIdIncrementer())
            .start(readNotaryDistrictsListStep())
            .build();
}

@Bean("updateNotaryListInfoJob")
public Job updateNotaryListInfoJob(){
    return jobs.get("updateNotaryListInfoJob")
            .incrementer(new RunIdIncrementer())
            .start(readNotaryListStep())
            .build();
}

然后在上一个测试类中有这个测试方法,它应该测试上一个updateNotary地区作业的整个流程:

@Test
public void givenReferenceOutput_whenJobExecuted_thenSuccess() throws Exception {
    // when
    JobExecution jobExecution = jobLauncherTestUtils.launchJob(defaultJobParameters());
    JobInstance actualJobInstance = jobExecution.getJobInstance();
    ExitStatus actualJobExitStatus = jobExecution.getExitStatus();
    
    Assert.assertEquals(actualJobInstance.getJobName(), "updateNotaryDistrictsJob");
}

问题是,以这种方式运行此测试方法时,我在堆栈跟踪中获得了此异常:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jobLauncherTestUtils': Unsatisfied dependency expressed through method 'setJob' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.batch.core.Job' available: expected single matching bean but found 2: updateNotaryDistrictsJob,updateNotaryListInfoJob
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:768) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:720) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1413) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.9.jar:5.3.9]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.9.jar:5.3.9]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-2.5.3.jar:2.5.3]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[spring-boot-2.5.3.jar:2.5.3]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) ~[spring-boot-2.5.3.jar:2.5.3]
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:123) ~[spring-boot-test-2.5.3.jar:2.5.3]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) ~[spring-test-5.3.9.jar:5.3.9]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) ~[spring-test-5.3.9.jar:5.3.9]
    ... 69 common frames omitted
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.batch.core.Job' available: expected single matching bean but found 2: updateNotaryDistrictsJob,updateNotaryListInfoJob
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1358) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.9.jar:5.3.9]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:760) ~[spring-beans-5.3.9.jar:5.3.9]
    ... 88 common frames omitted

它似乎无法识别我的两个工作豆中必须使用什么。

为什么?怎么了?我错过了什么?如何尝试解决此问题?

共有1个答案

莫英卓
2023-03-14

SpringBatchTest提供的JobLauncherTestUtils期望测试上下文中只有一个类型为Job的bean。这也记录在注释的java文档中。

如果您使用SpringBootTest和完整组件扫描,以便拾取多个作业bean,那么SpringBatchTest无法立即运行。

最简单的解决方案可能是删除SpringBatchTest,并使用jobLauncher启动作业。或者,您可以将测试拆分为多个测试类,并分别使用仅包含单个作业bean的测试上下文。

 类似资料:
  • 我正在处理一个Spring批处理应用程序,该应用程序包含两个不同的作业bean(表示两个不同的作业)。这两项工作都必须由我的应用程序执行(目前,它可以顺序和并行地完成。目前它不是那么重要)。 我会试着解释我的情况和遇到的问题是什么: 首先,我有一个配置类,其中声明了我的两个作业对象(以及相关步骤): 然后,在第一时间,我创建了另一个SpringBatchExampleJobLauncher启动器类

  • 我有以下工作要处理在一定的时间间隔或特别的基础上。 作业中的步骤如下: 我也想要用户界面,在那里我可以触发一个特别的基础上的工作,而且我应该能够提供参数从用户界面。 我想用Spring batch来完成这个任务,但它更多的是用于读->处理->写之类的工作。这里,在第一步中,我正在生成由第二步读取的数据。我不确定我是否还可以使用Spring batch来实现这个,或者有更好的方法来实现这个。

  • 我不确定我是否能清楚地解释我的情况。请建议您对spring-batch作业测试E2E流是否有其他更好的意见,以及您是否可以对上述方法提供任何清晰的说明,这将是有帮助的。

  • 我是Spring批的初学者,我用它开发了一个简单的项目。我得到了错误。 这是我的代码,我只有一个类: 感谢您帮助我找到此错误的主要原因

  • 我有一个简单的控制器,它接受文件路径的JSON字符串,并对这些文件运行spring批处理作业。为了实现spring batch,我遵循了一个教程,该教程最终将在https://github.com/michaelhoffmantech/patter-batch-loader中生成代码。 继续下去,直到它抛出StackOverflowError。 任何关于改变什么来修复此问题的建议或帮助都将不胜感激