无法连接到测试容器 Neo4J 实例?

Can't connect to a testcontainer Neo4J instance?

这是我的测试class:

@Testcontainers
@ReactiveDataNeo4jTest
internal class RepositoryIT {

    @Container
    private val container = KNeo4jContainer.instance

    @Test
    fun `should answer with One`() {
        val boltUrl = container.getBoltUrl()
        GraphDatabase.driver(
                boltUrl,
                AuthTokens.basic("neo4j", "123456"))
                .use { driver ->
                    driver.session().use { session ->
                        val res = session.run("OPTIONAL MATCH(n) RETURN 1 AS value")
                        val one = res.single().get("value").asInt()
                        assertThat(one).isEqualTo(2)
                    }
                }

    }

}

object KNeo4jContainer {

    val instance by lazy {
        startNeo4jContainer()
    }

    private fun startNeo4jContainer(): Neo4jContainer<*> =
            Neo4jContainer<Nothing>("neo4j:4.1.0").apply {
                withEnv("NEO4J_AUTH", "neo4j/123456")
                withExposedPorts(7687)
                withExposedPorts(7473)
                withExposedPorts(7474)
                start()
            }
}

当我检查 docker ps -a 时,它似乎已经开始了

  CONTAINER ID   IMAGE                               COMMAND                  CREATED              STATUS              PORTS                                                                       NAMES                                                     
 -------------- ----------------------------------- ------------------------ -------------------- ------------------- --------------------------------------------------------------------------- ---------------------------------------------------------- 
  102b11fa5c7c   neo4j:4.1.0                         "/sbin/tini -g -- /d…"   About a minute ago   Up About a minute   0.0.0.0:32791->7473/tcp, 0.0.0.0:32790->7474/tcp, 0.0.0.0:32789->7687/tcp   pensive_williams                                          
  92fac72190e0   testcontainersofficial/ryuk:0.3.0   "/app"                   About a minute ago   Up About a minute   0.0.0.0:32788->8080/tcp                                                     testcontainers-ryuk-ecc3bfaf-44ba-482d-ae5f-ddba9a70182f

但是我连接不上:

org.neo4j.driver.exceptions.ServiceUnavailableException: Unable to connect to localhost:7687, ensure the database is running and that there is a working network connection to it.

我可以 运行 通过 docker-compose up:

对以相同配置启动的容器进行测试
version: "3.1"
services:
  neo4j:
    image:  neo4j:4.1.0
    ports:
      # Http
      - "7474:7474"
      # Https
      - "7473:7473"
      # Bolt
      - "7687:7687"
    environment:
      # initial password reset
      "NEO4J_AUTH": "neo4j/secret"

但我认为这只是为了在开发测试时不必启动新容器。

我做错了什么?

编辑

在启动代码中设置管理员密码为空后,将 AuthTokens 更改为 AuthTokens.none() - 仍然没有成功。

编辑

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath/>
    </parent>
    <!-- groupId, artifactId, version, name and description ommitted -->
    <properties>
        <java.version>11</java.version>
        <kotlin.version>1.3.72</kotlin.version>
        <neo4j.test.port>7687</neo4j.test.port>
        <neo4j.test.auth>neo4j/secret</neo4j.test.auth>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-kotlin</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor.kotlin</groupId>
            <artifactId>reactor-kotlin-extensions</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlinx</groupId>
            <artifactId>kotlinx-coroutines-reactor</artifactId>
        </dependency>
        <dependency>
            <groupId>org.neo4j.springframework.data</groupId>
            <artifactId>spring-data-neo4j-rx-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency><dependency>
        <groupId>org.neo4j.springframework.data</groupId>
        <artifactId>spring-data-neo4j-rx-spring-boot-starter</artifactId>
        <version>1.1.1</version>
    </dependency>
        <dependency>
            <groupId>org.neo4j.driver</groupId>
            <artifactId>neo4j-java-driver</artifactId>
            <version>4.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.neo4j.springframework.data</groupId>
            <artifactId>spring-data-neo4j-rx-spring-boot-test-autoconfigure</artifactId>
            <version>1.1.1</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.neo4j.test</groupId>
                    <artifactId>neo4j-harness</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.4.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>5.4.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.4.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>testcontainers</artifactId>
            <version>1.14.3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>1.14.3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>neo4j</artifactId>
            <version>1.14.3</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
        <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <configuration>
                    <args>
                        <arg>-Xjsr305=strict</arg>
                    </args>
                    <compilerPlugins>
                        <plugin>spring</plugin>
                    </compilerPlugins>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-allopen</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>0.33.0</version>
                <configuration>
                    <images>
                        <image>
                            <name>neo4j:4.1.0</name>
                            <alias>neo4j</alias>
                            <run>
                                <env>
                                    <NEO4J_AUTH>${neo4j.test.auth}</NEO4J_AUTH>
                                </env>
                                <ports>
                                    <port>${neo4j.test.port}:3306</port>
                                </ports>
                                <wait>
                                    <!-- time based waiting is the only option, because TCP-based or log-based polling are not reliable.
                                   see https://github.com/fabric8io/docker-maven-plugin/issues/328
                                   Alternatively we can wait log- or tcp-based and try to connect for a while in the test code -->
                                    <time>8000</time>
                                </wait>
                            </run>
                        </image>
                    </images>
                </configuration>
                <executions>
                    <execution>
                        <id>start</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>start</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>stop</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>stop</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.18.1</version>
                <configuration>
                    <includes>
                        <include>**/*IT.*</include>
                    </includes>
                    <systemPropertyVariables>
                        <neo4j.port>${neo4j.test.port}</neo4j.port>
                        <neo4j.auth>${neo4j.test.auth}</neo4j.auth>
                    </systemPropertyVariables>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M4</version>
                <configuration>
                    <excludes>
                        <exclude>**/*IT.*</exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

解决方案

 private fun startNeo4jContainer(): Neo4jContainer<*> {
        val container = Neo4jContainer<Nothing>("neo4j:4.1.0").apply {
            withAdminPassword(null)
            start()
        }
        return container
    }

发生这种情况是因为在 Windows 中,默认地址 0.0.0.0 未转换为本地主机。

我无法重现,但添加这些环境变量应该可以完成工作:

version: "3.1"
services:
  neo4j:
    image:  neo4j:4.1.0
    ports:
      # Http
      - "7474:7474"
      # Https
      - "7473:7473"
      # Bolt
      - "7687:7687"
    environment:
      # initial password reset
      - NEO4J_AUTH=neo4j/secret
      - NEO4J_dbms_connector_https_advertised__address=localhost:7473
      - NEO4J_dbms_connector_http_advertised__address=localhost:7474
      - NEO4J_dbms_connector_bolt_advertised__address=localhost:7687

这与 https://www.testcontainers.org/features/networking/

的工作方式略有不同

From the host's perspective Testcontainers actually exposes this on a random free port. This is by design, to avoid port collisions that may arise with locally running software or in between parallel test runs.

以及 https://www.testcontainers.org/modules/databases/jdbc/

的底部

Now, in your test code (or a suitable setup method), you can obtain details necessary to connect to this database:

  • mysql.getJdbcUrl() provides a JDBC URL your code can connect to
  • mysql.getUsername() provides the username your code should pass to the driver
  • mysql.getPassword() provides the password your code should pass to the driver

考虑使用 Neo4jContainer#withAdminPassword 设置密码而不是设置环境变量。

否则这里会被覆盖: https://github.com/testcontainers/testcontainers-java/blob/6bdbc8ffe257fc879791eb7fddcf3b5ecccdd52d/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java#L118