【问题标题】:Using custom DB docker-image with Testcontainers将自定义 DB docker-image 与 Testcontainers 一起使用
【发布时间】:2019-03-28 19:43:22
【问题描述】:

我是 Testcontainers 的新手,所以我有一个问题。 我在 Spring/Hibernate 上有应用程序。我有 docker-image (h2testbase) 和 mysql-base (myTestDb) 和数据。我使用 -p 6161:3306 在 docker 中运行该图像。在 test/resources 目录中,我有文件 application.properties。它包含下一个

jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:6161/myTestDb?allowPublicKeyRetrieval=true&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Europe/Moscow&&useSSL=false
jdbc.username=root
jdbc.cred=admin
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
hibernate.format_sql=true

我使用 mvn test - 它正在工作。现在我想用 Testcontainers 运行这些测试。我在 pom.xml 中添加了依赖

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.9.1</version>
</dependency>
    
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>mysql</artifactId>
    <version>1.9.1</version>
    <scope>test</scope>
</dependency>

我扩展了 MySQLContainer 类

public class TestMySQL extends MySQLContainer {

    public TestMySQL() {
        super();
    }

    public TestMySQL(String dockerImageName) {
        super(dockerImageName);
    }

    @Override
    public String getDriverClassName() {
        return "com.mysql.cj.jdbc.Driver";
    }
}

因为 MySQLContainer 使用 com.mysql.jdbc.Driver 并且它已被弃用。 我的测试(例如)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
        HibernateConfiguration.class,
        SecurityConfiguration.class,
        SecurityInitializer.class,
        ViewConfiguration.class,
        ViewInitializer.class})
@WebAppConfiguration
public class ControllerServiceJTest {

    @ClassRule
    public static TestMySQL  container
            = new TestMySQL("h2testbase");

    @Autowired
    ControllerService controllerService;

    @Test
    public void stationPagination() {
        Map<String, Object> pag = controllerService.stationPagination(4);
        Assert.assertTrue(((List<Station>)pag.get("stations")).size() == 8);
    }

    @Test
    public void trainPagination() {
        Map<String, Object> pag = controllerService.trainPagination(1);
        Assert.assertTrue(((List<Train>)pag.get("trains")).size() == 20);
    }

    @Test
    public void switchHelper() {
        Assert.assertTrue(controllerService.stationSwitchHelper("BLUE").equals(URLs.REDIRECT_DASHSTATION + "/2"));
    }
}

我正在撞墙。如果我使用 mvn test,我会(通过 docker ps)看到容器已启动。它启动了两三次(并且映射在随机端口上进行,例如 328xx),但随后 maven 告诉

org.testcontainers.containers.ContainerLaunchException: Container startup failed
Caused by: org.rnorth.ducttape.RetryCountExceededException: Retry limit hit with exception
Caused by: org.testcontainers.containers.ContainerLaunchException: Could not create/start container
Caused by: org.rnorth.ducttape.TimeoutException: org.rnorth.ducttape.TimeoutException: java.util.concurrent.TimeoutException
Caused by: org.rnorth.ducttape.TimeoutException: java.util.concurrent.TimeoutException
Caused by: java.util.concurrent.TimeoutException

我现在该怎么办?如何告诉我的测试容器需要端口(6161)?如何使用 application.properties 中的参数?我找不到将自定义图像与数据库一起使用的代码示例。 提前谢谢你

更新 添加失败测试的完整结果。

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running ru.javasch.metro.junit.ControllerServiceJTest
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
SLF4J: Failed to load class "org.slf4j.impl.StaticMDCBinder".
SLF4J: Defaulting to no-operation MDCAdapter implementation.
SLF4J: See http://www.slf4j.org/codes.html#no_static_mdc_binder for further details.
        ?? Checking the system...
        ? Docker version should be at least 1.6.0
        ? Docker environment should have more than 2GB free disk space
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 97.189 s <<< FAILURE! - in ru.javasch.metro.junit.ControllerServiceJTest
[ERROR] ru.javasch.metro.junit.ControllerServiceJTest  Time elapsed: 97.187 s  <<< ERROR!
org.testcontainers.containers.ContainerLaunchException: Container startup failed
Caused by: org.rnorth.ducttape.RetryCountExceededException: Retry limit hit with exception
Caused by: org.testcontainers.containers.ContainerLaunchException: Could not create/start container
Caused by: org.rnorth.ducttape.TimeoutException: org.rnorth.ducttape.TimeoutException: java.util.concurrent.TimeoutException
Caused by: org.rnorth.ducttape.TimeoutException: java.util.concurrent.TimeoutException
Caused by: java.util.concurrent.TimeoutException

有一些信息。我尝试使用来自here 的 MySqlContainer 进行测试(使用我的 TestMySql)。当我使用干净的 mysql:5.5 图像时 - 一切都很好。但是当我尝试在容器中添加一些修改(例如 addFixedExposedPort)时,它没有启动,因为端口已经分配。如果我从脚本添加数据 - 它是“无法创建容器”。如果我尝试给它我的图像(h2testbase),再次“无法创建容器”。

【问题讨论】:

  • 如果您的独立 MySQL 正在运行,请停止或终止它。虽然暴露的端口可能不同,但如果我没记错的话,主机端口仍然是相同的 3306。如果可行,请告诉我,因为我稍后会将其作为答案发布。
  • @KarthikR 没有意义,因为经过测试我的应用程序连接到独立 MySQL

标签: java spring hibernate docker testcontainers


【解决方案1】:

看来你这里有两个问题。

  1. Docker 将 mysql 服务器暴露在一个随机端口上,但您需要一个固定端口。要解决这个问题,您可以使用addFixedExposedPort 的GenericContainer 设置固定端口

    public class TestMySQL extends MySQLContainer {
    
        public TestMySQL(String dockerImageName) {
            super(dockerImageName);
            addFixedExposedPort(6161, MYSQL_PORT);
        }
    
        @Override
        public String getDriverClassName() {
            return "com.mysql.cj.jdbc.Driver";
        }
    }
    
  2. 您可能没有数据库test、用户test 和密码test,因为它是MySQLContainer 中的默认凭据,这会导致您得到ContainerLaunchException。使用withDatabaseName、withUsername和withPassword正确配置DB和用户。

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {
        HibernateConfiguration.class,
        SecurityConfiguration.class,
        SecurityInitializer.class,
        ViewConfiguration.class,
        ViewInitializer.class})
    @WebAppConfiguration
    public class ControllerServiceJTest {
    
        @ClassRule
        public static TestMySQL  container
            = new TestMySQL("h2testbase")
              .withDatabaseName("myTestDb")
              .withUsername("root")
              .withPassword("admin");
    
        @Autowired
        ControllerService controllerService;
    
        @Test
        public void stationPagination() {
            Map<String, Object> pag = controllerService.stationPagination(4);
            Assert.assertTrue(((List<Station>)pag.get("stations")).size() == 8);
        }
    
        @Test
        public void trainPagination() {
            Map<String, Object> pag = controllerService.trainPagination(1);
            Assert.assertTrue(((List<Train>)pag.get("trains")).size() == 20);
        }
    
        @Test
        public void switchHelper() {
            Assert.assertTrue(controllerService.stationSwitchHelper("BLUE").equals(URLs.REDIRECT_DASHSTATION + "/2"));
        }
    }
    

更新:

要打开日志记录,请将以下依赖项添加到 pom.xml

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.1</version>
    </dependency>

并使用以下内容创建src/test/resources/logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT"/>
    </root>

    <logger name="org.testcontainers" level="DEBUG"/>
</configuration

【讨论】:

  • 还是一样的例外。还有一个问题。如何设置创建容器的时间限制?尝试需要 254.273 秒。但现在它不是创建两个或三个容器。只有一个,几乎立即关闭
  • 所以设置你可以使用的启动超时withStartupTimeoutSeconds
  • 您能分享执行日志吗?甚至为org.testcontainers 开启调试日志并分享它们
  • 实际上是上面的异常 - 这就是我在 maven 调试模式下所拥有的全部
  • 你能给出一个失败测试的完整输出吗?
猜你喜欢
  • 2020-09-06
  • 2017-09-30
  • 2017-07-04
  • 2018-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多