Spring Batch测试报错:找不到javax.sql.DataSource类型合格Bean
在对Spring Batch步骤做单元测试时,触发报错:
spring-boot-batch-test: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate
问题根源
@EnableBatchProcessing默认会启用依赖数据库的JobRepository组件,即使你的任务逻辑没用到数据库,Spring Batch核心元数据(任务执行记录等)默认需要数据库存储@SpringBatchTest注解会自动初始化JobRepositoryTestUtils,这个工具类必须依赖DataSource才能创建,上下文无数据源时就会抛出上述错误- 测试类同时使用
@DataJpaTest和@SpringBatchTest,两个注解的上下文切片逻辑可能冲突,导致自动配置的嵌入式数据源未生效
错误栈详情:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jobRepositoryTestUtils': Unsatisfied dependency expressed through method 'setDataSource' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:767) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:719) ~[spring-beans-5.3.22.jar:5.3.22]
...(省略后续栈信息)
解决方案
方案1:添加嵌入式内存数据库(快速解决)
这是最简便的方式,Spring Boot会自动配置嵌入式数据源,满足Spring Batch元数据存储需求:
- 引入H2嵌入式数据库依赖:
Maven:
Gradle:<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>test</scope> </dependency>testImplementation 'com.h2database:h2' - 调整测试类注解,移除不必要的
@DataJpaTest(若测试不需要JPA切片功能),避免注解冲突:@SpringBatchTest @ContextConfiguration(classes = { Scheduler.class }) @TestPropertySource("classpath:calculator-test.properties") @RunWith(SpringRunner.class) public class CalculatorTest { @Autowired private JobLauncherTestUtils jobLauncherTestUtils; // 若不需要操作JobRepository元数据,直接移除以下注入 // @Autowired private JobRepositoryTestUtils jobRepositoryTestUtils; @Test public void test_end_to_end() throws Exception { final JobExecution jobExecution = jobLauncherTestUtils.launchJob(); Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus()); } }
方案2:配置内存型JobRepository(彻底摆脱数据库依赖)
如果测试完全不需要持久化Batch元数据,可以配置基于内存的JobRepository,彻底移除数据源依赖:
- 创建测试专用配置类,替换默认的JobRepository组件:
@Configuration public class TestBatchConfig { @Bean public JobRepository jobRepository() throws Exception { MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean(); factory.setTransactionManager(new ResourcelessTransactionManager()); return factory.getObject(); } @Bean public JobLauncher jobLauncher(JobRepository jobRepository) { SimpleJobLauncher launcher = new SimpleJobLauncher(); launcher.setJobRepository(jobRepository); return launcher; } @Bean public JobBuilderFactory jobBuilderFactory(JobRepository jobRepository) { return new JobBuilderFactory(jobRepository); } @Bean public StepBuilderFactory stepBuilderFactory(JobRepository jobRepository, PlatformTransactionManager transactionManager) { return new StepBuilderFactory(jobRepository, transactionManager); } @Bean public PlatformTransactionManager transactionManager() { return new ResourcelessTransactionManager(); } } - 修改测试类,引入该测试配置:
注意:此方案下@SpringBatchTest @ContextConfiguration(classes = { Scheduler.class, TestBatchConfig.class }) @TestPropertySource("classpath:calculator-test.properties") @RunWith(SpringRunner.class) public class CalculatorTest { @Autowired private JobLauncherTestUtils jobLauncherTestUtils; @Test public void test_end_to_end() throws Exception { final JobExecution jobExecution = jobLauncherTestUtils.launchJob(); Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus()); } }JobRepositoryTestUtils依然需要数据源,若必须使用该工具类,建议改用方案1。
方案3:移除不必要的测试组件依赖
如果仅需验证Job执行结果,不需要操作JobRepository元数据,可直接移除JobRepositoryTestUtils的注入,减少不必要的依赖:
@SpringBatchTest @ContextConfiguration(classes = { Scheduler.class }) @TestPropertySource("classpath:calculator-test.properties") @RunWith(SpringRunner.class) public class CalculatorTest { @Autowired private JobLauncherTestUtils jobLauncherTestUtils; @Test public void test_end_to_end() throws Exception { final JobExecution jobExecution = jobLauncherTestUtils.launchJob(); Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus()); } }
此方案建议配合方案1使用,避免@SpringBatchTest自动初始化JobRepositoryTestUtils时再次触发报错。
内容的提问来源于stack exchange,提问作者OHA

