如何在多个 SpringBootTest 之间重用 Testcontainers?

Rei*_*der 5 java spring-boot spring-boot-test testcontainers

我正在使用带有 Spring Boot 的 TestContainers 为这样的存储库运行单元测试:

@Testcontainers
@ExtendWith(SpringExtension.class)
@ActiveProfiles("itest")
@SpringBootTest(classes = RouteTestingCheapRouteDetector.class)
@ContextConfiguration(initializers = AlwaysFailingRouteRepositoryShould.Initializer.class)
@TestExecutionListeners(listeners = DependencyInjectionTestExecutionListener.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Tag("docker")
@Tag("database")
class AlwaysFailingRouteRepositoryShould {

  @SuppressWarnings("rawtypes")
  @Container
  private static final PostgreSQLContainer database =
      new PostgreSQLContainer("postgres:9.6")
          .withDatabaseName("database")
          .withUsername("postgres")
          .withPassword("postgres");
Run Code Online (Sandbox Code Playgroud)

但是现在我有 14 个这样的测试,每次运行测试时都会启动一个新的 Postgres 实例。是否可以在所有测试中重用同一个实例?该Singleton模式并没有帮助,因为每次测试开始一个新的应用。

我也试过testcontainers.reuse.enable=true.testcontainers.properties.withReuse(true),但是这并没有帮助。

rie*_*pil 16

@Container如果您想拥有可重用的容器,则不能使用 JUnit Jupiter 注释。此注释确保在每次测试后停止容器。

您需要的是单例容器方法,并使用 eg@BeforeAll来启动您的容器。即使您进行.start()了多个测试,如果您选择在.withReuse(true)容器定义和.testcontainers.properties主目录中的以下文件中使用可重用性,Testcontainers 也不会启动新容器:

testcontainers.reuse.enable=true
Run Code Online (Sandbox Code Playgroud)

一个简单的示例可能如下所示:

@SpringBootTest
public class SomeIT {

  public static GenericContainer postgreSQLContainer = new PostgreSQLContainer().
    withReuse(true);

  @BeforeAll
  public static void beforeAll() {
    postgreSQLContainer.start();
  }

  @Test
  public void test() {

  }

}
Run Code Online (Sandbox Code Playgroud)

和另一个集成测试:

@SpringBootTest
public class SecondIT {

  public static GenericContainer postgreSQLContainer = new PostgreSQLContainer().
    withReuse(true);

  @BeforeAll
  public static void beforeAll() {
    postgreSQLContainer.start();
  }

  @Test
  public void secondTest() {

  }

}
Run Code Online (Sandbox Code Playgroud)

目前有一个PR 添加了关于此的文档

我整理了一篇博客文章,详细解释了如何使用 Testcontainers 重用容器

  • 是的,有多种方法可以配置此属性。查看 Testcontainers 文档的[配置位置部分](https://www.testcontainers.org/features/configuration/)。 (2认同)

Ely*_*mov 12

接受的答案很好,但问题是您仍然必须为每个集成测试重复配置(创建、启动等)。如果配置更简单,代码行数更少,那就更好了。我认为更干净的版本将使用 JUnit 5 扩展。

这就是我解决问题的方法。下面的示例使用 MariaDB 容器,但该概念适用于所有人。

  1. 创建容器配置持有类:
public class AppMariaDBContainer extends MariaDBContainer<AppMariaDBContainer> {

    private static final String IMAGE_VERSION = "mariadb:10.5";
    private static final String DATABASE_NAME = "my-db";
    private static final String USERNAME = "user";
    private static final String PASSWORD = "strong-password";

    public static AppMariaDBContainer container = new AppMariaDBContainer()
            .withDatabaseName(DATABASE_NAME)
            .withUsername(USERNAME)
            .withPassword(PASSWORD);

    public AppMariaDBContainer() {
        super(IMAGE_VERSION);
    }

}
Run Code Online (Sandbox Code Playgroud)
  1. 创建一个启动容器并设置DataSource属性的扩展类。如果需要的话运行迁移:
public class DatabaseSetupExtension implements BeforeAllCallback {

    @Override
    public void beforeAll(ExtensionContext context) {
        AppMariaDBContainer.container.start();
        updateDataSourceProps(AppMariaDBContainer.container);
        //migration logic here (if needed)
    }

    private void updateDataSourceProps(AppMariaDBContainer container) {
        System.setProperty("spring.datasource.url", container.getJdbcUrl());
        System.setProperty("spring.datasource.username", container.getUsername());
        System.setProperty("spring.datasource.password", container.getPassword());
    }

}
Run Code Online (Sandbox Code Playgroud)
  1. 添加@ExtendWith到您的测试类
@SpringBootTest
@ExtendWith(DatabaseSetupExtension.class)
class ApplicationIntegrationTests {

    @Test
    void someTest() {
    }

}

Run Code Online (Sandbox Code Playgroud)

另一个测试

@SpringBootTest
@ExtendWith(DatabaseSetupExtension.class)
class AnotherIntegrationTests {

    @Test
    void anotherTest() {
    }

}
Run Code Online (Sandbox Code Playgroud)


brn*_*eto 10

如果您决定继续使用单例模式,请注意“通过 JDBC URL 方案启动的数据库容器”中的警告。我花了几个小时才注意到,即使我使用的是单例模式,总是会创建一个映射在不同端口上的附加容器。

jdbc:tc:postgresql:<image-tag>:///<databasename>总之,如果需要使用单例模式,请不要使用测试容器 JDBC(无主机)URI,例如。


mpk*_*nje 5

使用单例容器或可重用容器都是可能的解决方案,但因为它们不将容器的生命周期限定在应用程序上下文的生命周期内,所以这两种方法都不太理想。

不过,可以通过使用 a 将容器的范围限定到应用程序上下文生命周期,ContextCustomizerFactory已在博客文章中更详细地介绍了这一点

在测试中使用:

@Slf4j
@SpringBootTest
@EnabledPostgresTestContainer
class DemoApplicationTest {

    @Test
    void contextLoads() {
        log.info("Hello world");
    }

}
Run Code Online (Sandbox Code Playgroud)

然后启用注释META-INF/spring.factories

org.springframework.test.context.ContextCustomizerFactory=\
  com.logarithmicwhale.demo.EnablePostgresTestContainerContextCustomizerFactory
Run Code Online (Sandbox Code Playgroud)

可以实现为:

org.springframework.test.context.ContextCustomizerFactory=\
  com.logarithmicwhale.demo.EnablePostgresTestContainerContextCustomizerFactory
Run Code Online (Sandbox Code Playgroud)