Spring Batch测试:如何临时禁用DataSource模拟数据库连接断开?
嘿,这个场景我之前在Spring Batch测试里碰到过,给你几个靠谱的方案,既能单独控制某个测试的DataSource行为,又完全不会干扰其他依赖H2内存库的测试:
方案1:用@MockBean直接替换DataSource(最推荐)
Spring Boot的@MockBean注解可以帮你在单个测试类/方法里,把容器中原有的DataSource Bean替换成Mock对象。这样这个测试会用Mock的DataSource,其他测试还是正常使用你的H2配置,完全隔离。
你只需要在目标测试类里添加@MockBean,然后模拟获取连接时抛出异常,就能完美复现数据库断开的场景:
import org.junit.jupiter.api.Test; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import javax.sql.DataSource; import java.sql.SQLException; import static org.mockito.Mockito.when; @SpringBootTest public class BatchJobConnectionFailureTest { @Autowired private JobLauncher jobLauncher; @Autowired private Job myBatchJob; // 替换容器里的DataSource为Mock对象 @MockBean private DataSource dataSource; @Test void testJobBehaviorWhenConnectionFails() throws Exception { // 模拟获取连接时抛出异常,复现断开场景 when(dataSource.getConnection()).thenThrow(new SQLException("Database connection lost")); // 执行Job并断言预期的失败行为 JobExecution jobExecution = jobLauncher.run(myBatchJob, /* 传入你的Job参数 */); // 示例断言:比如检查Job的退出状态是否符合预期 // assertEquals("FAILED", jobExecution.getExitStatus().getExitCode()); } }
这个方法的好处是简单直接,不需要额外配置,而且Mock的范围只限于当前测试类,完全不会影响其他测试。
方案2:用Profile隔离自定义无效DataSource
如果需要更灵活的自定义(比如模拟更复杂的连接失败逻辑),可以创建一个测试专用的配置类,通过Spring Profile来激活,这样只有指定的测试会用这个无效的DataSource。
首先创建一个带Profile的配置类:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; @Configuration @Profile("connection-failure-test") public class FailedDataSourceConfig { @Bean public DataSource dataSource() { return new DataSource() { // 核心:获取连接时直接抛出异常 @Override public Connection getConnection() throws SQLException { throw new SQLException("Simulated database connection failure"); } @Override public Connection getConnection(String username, String password) throws SQLException { throw new SQLException("Simulated database connection failure"); } // 其他DataSource接口方法可以默认实现或者抛出UnsupportedOperationException @Override public <T> T unwrap(Class<T> iface) throws SQLException { throw new UnsupportedOperationException(); } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; } // 省略其他方法的实现... }; } }
然后在目标测试类上激活这个Profile:
import org.junit.jupiter.api.Test; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @SpringBootTest @ActiveProfiles("connection-failure-test") public class BatchJobConnectionFailureTest { @Autowired private JobLauncher jobLauncher; @Autowired private Job myBatchJob; @Test void testJobBehaviorWhenConnectionFails() throws Exception { // 执行Job并断言失败行为 JobExecution jobExecution = jobLauncher.run(myBatchJob, /* 传入参数 */); // 添加你的断言逻辑 } }
这个方案适合需要自定义DataSource行为的场景,而且通过Profile完全隔离,其他测试只要不激活这个Profile,就还是用原来的H2配置。
方案3:用@DirtiesContext重置上下文(谨慎使用)
如果上面两个方案都不适用,可以考虑用@DirtiesContext标记测试,让Spring在测试结束后重置上下文,避免这个测试的Bean修改影响后续测试。不过这个方法会增加测试时间,因为每次都要重建上下文,所以只在必要时使用。
示例:
import org.junit.jupiter.api.Test; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; @SpringBootTest // 测试后重置上下文 @DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD) public class BatchJobConnectionFailureTest { @Autowired private JobLauncher jobLauncher; @Autowired private Job myBatchJob; // 这里可以手动替换或修改DataSource,测试后上下文被清理 @Test void testJobBehaviorWhenConnectionFails() throws Exception { // 执行测试逻辑 } }
总结一下:优先用方案1(@MockBean),简单高效;如果需要自定义DataSource行为,选方案2(Profile);方案3作为最后的备选。这样就能完美实现单个测试的隔离,模拟数据库断开的边缘场景,同时不影响其他依赖H2的测试。
内容的提问来源于stack exchange,提问作者rnmalone

