MongoDB Atlas Local容器集成测试连接失败问题求助
我正在为一个响应式服务编写集成测试,但在连接MongoDB Atlas Local测试容器时始终失败,两种场景下分别抛出不同异常:
- 添加
@ServiceConnection注解时:
Caused by: org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsNotFoundException: No ConnectionDetails found for source '@ServiceConnection source for MovieServiceIT.mongoDBContainer'
- 移除
@ServiceConnection注解时:
org.springframework.dao.DataAccessResourceFailureException: Timed out while waiting for a server that matches WritableServerSelector. Client view of cluster state is {type=UNKNOWN, servers=[{address=localhost:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketOpenException: Exception opening socket}, caused by {io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/[0:0:0:0:0:0:0:1]:27017}, caused by {java.net.ConnectException: Connection refused}}]
测试类代码(MovieServiceIT)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Testcontainers @ExtendWith(SpringExtension.class) @AutoConfigureWebTestClient @TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @DisplayName("Testing Movies API") class MovieServiceIT { @Container @ServiceConnection static MongoDBAtlasLocalContainer mongoDBContainer = new MongoDBAtlasLocalContainer("mongodb/mongodb-atlas-local:8.0.3"); @Autowired private WebTestClient webTestClient; @Autowired private MoviesRepository moviesRepository; @BeforeEach void setUp() throws IOException { assertThat(mongoDBContainer.isCreated()).isTrue(); assertThat(mongoDBContainer.isRunning()).isTrue(); movies = DataLoadUtil.loadTestModels("resources/data/movies.json"); // This works moviesRepository.deleteAll() .thenMany( Flux.just(movies.toArray(new Movie[]{})).flatMap(moviesRepository::save) ) .blockLast(); } @Test @Order(1) @DisplayName("Endpoint: GET /movies/{id}") void getMovieById() throws Exception { webTestClient.get() .uri("/movies/573a1392f29313caabcdae3d") .exchange() .expectStatus().isOk() .expectBody(Movie.class) .consumeWith(response -> { assertEquals("Modern Times", Objects.requireNonNull(response.getResponseBody()).getTitle()); }); } }
pom.xml主要依赖配置
<dependencies> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-otlp</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency> <!-- TESTING //--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-testcontainers</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>mongodb</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-docker-compose</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-webtestclient</artifactId> <scope>test</scope> </dependency> <!-- BUILD //--> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-common</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-mockmvc</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.swagger.core.v3</groupId> <artifactId>swagger-models</artifactId> <version>2.2.9</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </path> </annotationProcessorPaths> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-maven-plugin</artifactId> <version>2.2.1</version> <executions> <execution> <id>generate-docs</id> <phase>prepare-package</phase> <goals> <goal>process-asciidoc</goal> </goals> <configuration> <backend>html</backend> <doctype>book</doctype> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-asciidoctor</artifactId> <version>3.0.1</version> </dependency> </dependencies> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>${surefire-plugin.version}</version> <dependencies> <dependency> <groupId>me.fabriciorby</groupId> <artifactId>maven-surefire-junit5-tree-reporter</artifactId> <version>1.3.0</version> </dependency> </dependencies> <configuration> <reportFormat>plain</reportFormat> <systemPropertyVariables> <java.util.logging.manager>org.apache.logging.log4j.jul.LogManager</java.util.logging.manager> <maven.home>${maven.home}</maven.home> </systemPropertyVariables> <statelessTestsetReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5Xml30StatelessReporter"> <disable>false</disable> <version>3.0</version> <usePhrasedFileName>false</usePhrasedFileName> <usePhrasedTestSuiteClassName>true</usePhrasedTestSuiteClassName> <usePhrasedTestCaseClassName>true</usePhrasedTestCaseClassName> <usePhrasedTestCaseMethodName>true</usePhrasedTestCaseMethodName> </statelessTestsetReporter> <consoleOutputReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5ConsoleOutputReporter"> <disable>true</disable> </consoleOutputReporter> <statelessTestsetInfoReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter"> <disable>false</disable> <usePhrasedFileName>false</usePhrasedFileName> <usePhrasedClassNameInRunning>true</usePhrasedClassNameInRunning> <usePhrasedClassNameInTestCaseSummary>true</usePhrasedClassNameInTestCaseSummary> </statelessTestsetInfoReporter> </configuration> </plugin> </plugins> </build>
解决方案
1. 替换@ServiceConnection,手动注入连接属性
MongoDB Atlas Local容器和Spring Boot的@ServiceConnection注解存在兼容性问题,Spring Boot无法自动识别该容器的连接详情。需要移除@ServiceConnection,改用@DynamicPropertySource手动注入连接信息:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Testcontainers @AutoConfigureWebTestClient @TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @DisplayName("Testing Movies API") class MovieServiceIT { @Container static MongoDBAtlasLocalContainer mongoDBContainer = new MongoDBAtlasLocalContainer("mongodb/mongodb-atlas-local:8.0.3"); // 新增动态属性配置 @DynamicPropertySource static void setMongoProperties(DynamicPropertyRegistry registry) { // 使用副本集URL连接Atlas Local容器 registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl); // 可选:指定测试数据库名 // registry.add("spring.data.mongodb.database", () -> "movie_test_db"); } // 其余代码保持不变 }
2. 等待容器完全就绪
MongoDB Atlas Local容器启动速度慢于普通MongoDB容器,需要在测试前显式等待容器就绪:
@BeforeEach void setUp() throws IOException, InterruptedException { // 等待容器日志出现就绪标识 mongoDBContainer.waitingFor(Wait.forLogMessage(".*Waiting for connections.*", 1)); assertThat(mongoDBContainer.isCreated()).isTrue(); assertThat(mongoDBContainer.isRunning()).isTrue(); movies = DataLoadUtil.loadTestModels("resources/data/movies.json"); moviesRepository.deleteAll() .thenMany(Flux.just(movies.toArray(new Movie[]{})).flatMap(moviesRepository::save)) .blockLast(); }
3. 禁用Docker Compose自动配置(可选)
如果测试不需要使用spring-boot-docker-compose,建议在测试类中禁用,避免和Testcontainers冲突:
@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "spring.boot.docker.compose.enabled=false" )
4. 校验依赖版本兼容性
确保Spring Boot和Testcontainers版本匹配:
- Spring Boot 3.x 对应 Testcontainers 1.19+
- Spring Boot 2.x 需使用Testcontainers 1.18及以下兼容版本
检查pom.xml中spring-boot-testcontainers和org.testcontainers:mongodb的版本是否一致,建议统一使用Testcontainers最新稳定版。
内容的提问来源于stack exchange,提问作者John Manko

