无法从 junit 为 spring 批处理作业加载应用程序上下文

cannot load application context from junit for spring batch job

我在为 spring 批测试从 junit 测试加载应用程序上下文时遇到问题。我参考了 spring 文档 https://docs.spring.io/spring-batch/trunk/reference/html/testing.html 以及我可以在网上找到的所有可用信息,但仍然无法获得简单的 junit 测试工作。

我正在使用注释来加载应用上下文,代码如下。我的目标是能够测试各个步骤。

我还从网络上克隆了一些示例,但是当 运行 在我的本地计算机上时,它们会给我同样的错误 - 无法加载应用程序上下文...这让我开始思考这些测试应该如何进行运行? 运行 As -> junit tests 是如何对所有单元进行测试的运行...不是吗?

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {BatchProcessingConfiguration.class, RestTemplateConfig.class, SftpConfig.class, DbConfig.class, RuntimeSessionFactoryLocator.class}, 
                      loader = AnnotationConfigContextLoader.class)
public class BatchProcessingJobApplicationTests extends AbstractTransactionalJUnit4SpringContextTests {

   @Autowired
   private JobLauncherTestUtils jobLauncherTestUtils;

   @Bean
   public DataSource dataSource() {
      DriverManagerDataSource dataSource = new DriverManagerDataSource();
      dataSource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
      dataSource.setUrl("url");
      dataSource.setUsername("username");
      dataSource.setPassword("password");

      return dataSource;
   }



   @Test
   public void testJob() throws Exception {

      JobExecution jobExecution = jobLauncherTestUtils.launchJob();

      Assert.assertEquals("COMPLETED", jobExecution.getExitStatus().getExitCode());
   }


}

代码来自 spring 文档。

我是新手。感谢您的帮助。

堆栈跟踪:

java.lang.IllegalStateException: Failed to load ApplicationContext 
   at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) 
   at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) 

... ...

工作代码:

 @RunWith(SpringRunner.class)
    @SpringBatchTest
    @ContextConfiguration(classes = {BatchProcessingJobApplication.class, DataSourceConfiguration.class, JobconfigurationTest.class, BatchProperties.class}, initializers=ConfigFileApplicationContextInitializer.class)
    @ActiveProfiles("test")
    public class BatchProcessingJobApplicationTests {

       @Autowired
       private JobLauncherTestUtils jobLauncherTestUtils;

    @Test
public void testStep() throws Exception {
    // given
    JobParameters jobParameters = jobLauncherTestUtils.getUniqueJobParameters();

    // when
    JobExecution jobExecution = jobLauncherTestUtils.launchStep("jobName", jobParameters);

    // then
    Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
}

    }


    public class DataSourceConfiguration {
    @Bean
        public DataSource dataSource(){

            SQLServerConnectionPoolDataSource dataSource = new SQLServerConnectionPoolDataSource();
              dataSource.setURL(url);
              dataSource.setUser(username);
              dataSource.setPassword(password);
              return dataSource;
        }
    }

    @Configuration
    public class JobconfigurationTest {
          @Bean
          public JobLauncherTestUtils jobLauncherTestUtils() {
                return new JobLauncherTestUtils();
          }

    }

希望这对像我这样的人有所帮助。

My aim is to be able to test individual steps.

您可以使用 JobLauncherTestUtils#launchStep(String stepName) 启动特定步骤而不是整个作业。如果您想对特定步骤进行单元测试,我建议仅导入测试该步骤所需的配置(我指的是 RestTemplateConfigSftpConfig 等,除非该步骤确实需要这些配置)。

请注意,被测作业应在测试上下文中声明为 bean,因为它 is autowiredJobLauncherTestUtils 中。在您的情况下,我假设它是在 BatchProcessingConfiguration class.

中定义的

典型的步骤测试类似于:

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.batch.test.context.SpringBatchTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBatchTest
@ContextConfiguration(classes = {JobConfiguration.class}) // contains the job/step definition
public class JobTest {

   @Autowired
   private JobLauncherTestUtils jobLauncherTestUtils;

   @Test
   public void testStep() throws Exception {
      // given
      JobParameters jobParameters = jobLauncherTestUtils.getUniqueJobParameters();

      // when
      JobExecution jobExecution = jobLauncherTestUtils.launchStep("myStep", jobParameters);

      // then
      Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
   }

}

注意:SpringBatchTest 在 v4.1 中已 added

编辑以获取更多评论: 当应用程序上下文包含多个作业 bean 时,无法知道在 JobLauncherTestUtils 中注入哪一个。在这种情况下,应该手动创建 JobLauncherTestUtils 并使用被测作业进行配置。这是一个例子:

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.JobParametersValidator;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.job.DefaultJobParametersValidator;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

/*
 * This test class shows how to use the JobLauncherTestUtils when the application
 * context contains multiple jobs. Since the job is autowired in JobLauncherTestUtils (see setter),
 * it is not possible to autowire one job (ambiguous injection). Hence, it is required to either:
 *  - Not autowire the JobLauncherTestUtils (as shown in this class)
 *  - Or separate job definitions and use a test class for each job (better solution IMO, each job has its own test)
 */
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestMultipleJobsWithJobLauncherTestUtils.JobsConfiguration.class)
public class TestMultipleJobsWithJobLauncherTestUtils {

    // don't autowire the JobLauncherTestUtils in this case otherwise No qualifying bean of type 'org.springframework.batch.core.Job' available: expected single matching bean but found 2: job1,job2
    private JobLauncherTestUtils jobLauncherTestUtils = new JobLauncherTestUtils();

    @Autowired
    private Job job1;

    @Autowired
    private Job job2;

    @Autowired
    private JobLauncher jobLauncher;
    @Autowired
    private JobRepository jobRepository;

    @Before
    public void setUp() {
        jobLauncherTestUtils.setJobLauncher(jobLauncher);
        jobLauncherTestUtils.setJobRepository(jobRepository);
    }

    @Test
    public void testJob1() throws Exception {
        // given
        jobLauncherTestUtils.setJob(job1);

        // when
        JobExecution jobExecution = jobLauncherTestUtils.launchJob();

        // then
        Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
    }

    @Test
    public void testJob2() throws Exception {
        // given
        jobLauncherTestUtils.setJob(job2);

        // when
        JobExecution jobExecution = jobLauncherTestUtils.launchJob();

        // then
        Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
    }

    @Configuration
    @EnableBatchProcessing
    public static class JobsConfiguration {

        @Bean
        public Job job1() {
            return new SimpleJob("job1");
        }

        @Bean
        public Job job2() {
            return new SimpleJob("job2");
        }

        // Don't declare the JobLauncherTestUtils as a bean to avoid dependecy injection
//      @Bean
//      public JobLauncherTestUtils jobLauncherTestUtils() {
//          return new JobLauncherTestUtils();
//      }

    }

    static class SimpleJob implements Job {

        private String name;

        public SimpleJob(String name) {
            this.name = name;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public boolean isRestartable() {
            return false;
        }

        @Override
        public void execute(JobExecution execution) {
            System.out.println("Executing job " + this.name);
            execution.setExitStatus(ExitStatus.COMPLETED);
        }

        @Nullable
        @Override
        public JobParametersIncrementer getJobParametersIncrementer() {
            return null;
        }

        @Override
        public JobParametersValidator getJobParametersValidator() {
            return new DefaultJobParametersValidator();
        }

    }
}

希望这对您有所帮助。