最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

spring - No ConnectionDetails found for source @ServiceConnection MongoDBAtlasLocalContainer - Stack Overflow

programmeradmin5浏览0评论

I'm attempting to write ITs for a simple reactive service, but the test fails when attempt to obtain the test container (MongoDB Atlas Local) connection.

The error:

Caused by: .springframework.boot.autoconfigure.service.connection.ConnectionDetailsNotFoundException: No ConnectionDetails found for source '@ServiceConnection source for MovieServiceIT.mongoDBContainer'

Removing @ServiceConnection yields:

.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 {ioty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/[0:0:0:0:0:0:0:1]:27017}, caused by {java.ConnectException: Connection refused}}]


Here is 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());
                });
    }


}


Here is most of the pom.xml:


    <dependencies>

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-otlp</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

        <!-- TESTING //-->

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>.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>.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>.testcontainers</groupId>
            <artifactId>mongodb</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-docker-compose</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>.springframework.restdocs</groupId>
            <artifactId>spring-restdocs-webtestclient</artifactId>
            <scope>test</scope>
        </dependency>


        <!-- BUILD //-->
        <dependency>
            <groupId>.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-common</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.8.0</version>
        </dependency>

        <dependency>
            <groupId>.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>.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>.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>

            <plugin>
                <groupId>.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>.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>.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>.apache.logging.log4j.jul.LogManager</java.util.logging.manager>
                        <maven.home>${maven.home}</maven.home>
                    </systemPropertyVariables>
                    <statelessTestsetReporter implementation=".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=".apache.maven.plugin.surefire.extensions.junit5.JUnit5ConsoleOutputReporter">
                        <disable>true</disable>
                    </consoleOutputReporter>
                    <statelessTestsetInfoReporter implementation=".apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
                        <disable>false</disable>
                        <usePhrasedFileName>false</usePhrasedFileName>
                        <usePhrasedClassNameInRunning>true</usePhrasedClassNameInRunning>
                        <usePhrasedClassNameInTestCaseSummary>true</usePhrasedClassNameInTestCaseSummary>
                    </statelessTestsetInfoReporter>
                </configuration>
            </plugin>
        </plugins>
    </build>

I'm attempting to write ITs for a simple reactive service, but the test fails when attempt to obtain the test container (MongoDB Atlas Local) connection.

The error:

Caused by: .springframework.boot.autoconfigure.service.connection.ConnectionDetailsNotFoundException: No ConnectionDetails found for source '@ServiceConnection source for MovieServiceIT.mongoDBContainer'

Removing @ServiceConnection yields:

.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 {ioty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/[0:0:0:0:0:0:0:1]:27017}, caused by {java.ConnectException: Connection refused}}]


Here is 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());
                });
    }


}


Here is most of the pom.xml:


    <dependencies>

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-otlp</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

        <!-- TESTING //-->

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>.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>.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>.testcontainers</groupId>
            <artifactId>mongodb</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>.springframework.boot</groupId>
            <artifactId>spring-boot-docker-compose</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>.springframework.restdocs</groupId>
            <artifactId>spring-restdocs-webtestclient</artifactId>
            <scope>test</scope>
        </dependency>


        <!-- BUILD //-->
        <dependency>
            <groupId>.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-common</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.8.0</version>
        </dependency>

        <dependency>
            <groupId>.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>.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>.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>

            <plugin>
                <groupId>.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>.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>.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>.apache.logging.log4j.jul.LogManager</java.util.logging.manager>
                        <maven.home>${maven.home}</maven.home>
                    </systemPropertyVariables>
                    <statelessTestsetReporter implementation=".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=".apache.maven.plugin.surefire.extensions.junit5.JUnit5ConsoleOutputReporter">
                        <disable>true</disable>
                    </consoleOutputReporter>
                    <statelessTestsetInfoReporter implementation=".apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
                        <disable>false</disable>
                        <usePhrasedFileName>false</usePhrasedFileName>
                        <usePhrasedClassNameInRunning>true</usePhrasedClassNameInRunning>
                        <usePhrasedClassNameInTestCaseSummary>true</usePhrasedClassNameInTestCaseSummary>
                    </statelessTestsetInfoReporter>
                </configuration>
            </plugin>
        </plugins>
    </build>
Share Improve this question edited yesterday John Manko asked yesterday John MankoJohn Manko 1,9081 gold badge28 silver badges57 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Looking at the Testcontainers page in the spring documentation, there is no mention of ConnectionDetails being provided for the MongoDBAtlasLocalContainer.

So it looks like we need to provide a custom implementation of ConnectionDetails.

To implement ConnectionDetails for MongoDBAtlasLocalContainer myself, I referenced the test code implemented in Spring AI.

Steps to Implement ConnectionDetails for MongoDBAtlasLocalContainer

1. Create a MongoDbAtlasLocalContainerConnectionDetailsFactory

Below is an example implementation inspired by the Spring AI project (The code below can be found at the link).

class MongoDbAtlasLocalContainerConnectionDetailsFactory
        extends ContainerConnectionDetailsFactory<MongoDBAtlasLocalContainer, MongoConnectionDetails> {

    @Override
    protected MongoConnectionDetails getContainerConnectionDetails(
            ContainerConnectionSource<MongoDBAtlasLocalContainer> source) {
        return new MongoDbAtlasLocalContainerConnectionDetails(source);
    }

    /**
     * {@link MongoConnectionDetails} backed by a {@link ContainerConnectionSource}.
     */
    private static final class MongoDbAtlasLocalContainerConnectionDetails
            extends ContainerConnectionDetails<MongoDBAtlasLocalContainer> implements MongoConnectionDetails {

        private MongoDbAtlasLocalContainerConnectionDetails(
                ContainerConnectionSource<MongoDBAtlasLocalContainer> source) {
            super(source);
        }

        @Override
        public ConnectionString getConnectionString() {
            return new ConnectionString(getContainer().getConnectionString());
        }

    }

}

2. Register the ConnectionDetailsFactory in spring.factories

Create a spring.factories file in /resources/META-INF/spring.factories and enter the following contents

.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\
.example.demo.MongoDbAtlasLocalContainerConnectionDetailsFactory

Replace .example.demo with the actual package name where your factory exists

3. Proceed with the test.

Here's an example test to verify the test container works:

@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MovieServiceIT {

    @Container
    static MongoDBAtlasLocalContainer mongoDBContainer = new MongoDBAtlasLocalContainer("mongodb/mongodb-atlas-local:8.0.3");

    private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
            .withPropertyValues("spring.data.mongodb.database=springsample",
                    "spring.data.mongodb.uri=" + mongoDBContainer.getConnectionString());

    @Test
    void test() {
        this.contextRunner.run(context -> {
            assertThat(mongoDBContainer.isCreated()).isTrue();
            assertThat(mongoDBContainer.isRunning()).isTrue();
        });
    }
}
发布评论

评论列表(0)

  1. 暂无评论