如何测试连接多数据源的Spring Boot应用?
Hey there, let's tackle this multi-data-source testing issue you're facing with Spring Boot. Mockito alone might struggle to distinguish between your two distinct data sources, but we've got several solid approaches to get your tests working smoothly.
First off, I assume you've already tagged your two data sources with @Qualifier in your main configuration (if not, you should start there). Here's what that might look like:
@Configuration public class DataSourceConfig { @Bean(name = "firstDataSource") @ConfigurationProperties(prefix = "spring.datasource.first") public DataSource firstDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "secondDataSource") @ConfigurationProperties(prefix = "spring.datasource.second") public DataSource secondDataSource() { return DataSourceBuilder.create().build(); } }
In your tests, you can pair @MockBean with @Qualifier to explicitly target each data source—no more confusion for Mockito:
@SpringBootTest public class MyServiceTest { @MockBean @Qualifier("firstDataSource") private DataSource firstDataSource; @MockBean @Qualifier("secondDataSource") private DataSource secondDataSource; @Autowired private MyService myService; // Your test logic here... }
This tells Mockito exactly which bean to replace, so it won't mix up your two data sources.
If you need real database interactions instead of mocks, swap your production data sources with embedded H2 databases for testing. Create a src/test/resources/application-test.properties file with:
# First embedded H2 data source spring.datasource.first.url=jdbc:h2:mem:firstdb;DB_CLOSE_DELAY=-1 spring.datasource.first.driver-class-name=org.h2.Driver spring.datasource.first.username=sa spring.datasource.first.password= # Second embedded H2 data source spring.datasource.second.url=jdbc:h2:mem:seconddb;DB_CLOSE_DELAY=-1 spring.datasource.second.driver-class-name=org.h2.Driver spring.datasource.second.username=sa spring.datasource.second.password=
Then activate this profile in your test class:
@SpringBootTest @ActiveProfiles("test") public class MyIntegrationTest { @Autowired @Qualifier("firstRepository") private FirstRepository firstRepo; @Autowired @Qualifier("secondRepository") private SecondRepository secondRepo; // Test real CRUD operations here without mock confusion }
This approach lets you validate actual data logic without dealing with Mockito's limitations on bean differentiation.
For maximum flexibility, create a dedicated test configuration class where you define exactly what data sources (mocked or embedded) to use:
@TestConfiguration public class TestDataSourceConfig { @Bean(name = "firstDataSource") public DataSource firstTestDataSource() { // Return a mocked DataSource return Mockito.mock(DataSource.class); } @Bean(name = "secondDataSource") public DataSource secondTestDataSource() { // Or use an embedded H2 database for real interactions return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath:second-db-schema.sql") .build(); } }
Import this config into your test to override the production beans:
@SpringBootTest @Import(TestDataSourceConfig.class) public class MyCustomConfigTest { // Your test logic with full control over data sources }
This gives you complete freedom to tailor each data source's behavior to your test scenario.
Often, you don't need to mock the data source directly—mock the repositories/mappers tied to each data source instead. Just make sure your repositories are tagged with @Qualifier to match their data source:
@SpringBootTest public class MyServiceTest { @MockBean @Qualifier("firstRepository") private FirstRepository firstRepository; @MockBean @Qualifier("secondRepository") private SecondRepository secondRepository; @Autowired private MyService myService; @Test public void testBusinessLogic() { // Set up mock behavior for each repository when(firstRepository.findById(anyLong())).thenReturn(Optional.of(new FirstEntity())); when(secondRepository.save(any(SecondEntity.class))).thenReturn(new SecondEntity()); // Execute test and assert results var result = myService.performAction(1L); assertNotNull(result); } }
This approach focuses on testing your business logic rather than low-level data source interactions, and it avoids Mockito's bean confusion entirely.
内容的提问来源于stack exchange,提问作者vivek gupta

