使用 Testcontainer 进行 Spring 集成测试 - 数据库在应用程序之后启动
Posted
技术标签:
【中文标题】使用 Testcontainer 进行 Spring 集成测试 - 数据库在应用程序之后启动【英文标题】:Spring integration Testing with Testcontainer - Database starting after Application 【发布时间】:2020-11-24 14:38:43 【问题描述】:我用spring(core)测试的App想通过testcontainer连接数据库,但是此时testcontainer没有启动。如何实现测试容器准备就绪,然后启动整个上下文?
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@Testcontainers
@SpringJUnitWebConfig(locations = "classpath:it-context.xml")
public class IntegrationTest
@Container
private PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer();
@Autowired
private StatusBean status;
@Test
public void appStartedSuccessful()
// app & database successful started and connected
System.out.println(postgresqlContainer.getJdbcUrl());
System.out.println(postgresqlContainer.getTestQueryString());
System.out.println(status.getStartupTimestamp());
【问题讨论】:
您是否可能不小心使用了 JUnit 4 中的@Test
?如果没有,您可以添加测试和所有导入的完整堆栈跟踪吗?也许本指南也可能有所帮助:rieckpil.de/…
唯一的解决方案是将容器设置为“最终”和“静态”,以便在测试开始前创建一次数据库。
【参考方案1】:
您可能希望等待容器启动并准备好
查看documentation 尽管code 似乎表明容器应该正在等待一个神奇的日志条目,也许由于某种原因这对你不起作用。
如果你在你的测试方法中Thread.sleep
,Pg容器最终是否如预期到达?容器日志中的任何内容都表明某种失败(不太可能,因为 static
技巧对您有用)。
我在JUnit5 integration docs 中没有看到任何表明使用@Container
时需要手动等待的任何内容。
【讨论】:
问题是 spring 上下文在测试容器之前启动 - 即使等待。通过注释魔术 spring 将启动上下文,然后缺少数据库。 啊,是的,我觉得这听起来很熟悉。有人可能会说,您的应用程序应该能够在短时间内处理丢失的数据库,也许要等到它准备好后再开始启动。但是,您的解决方案现在可以正常工作,这可能更重要。 TestContainers 记录了这种静态启动技术,尽管机制稍微复杂一些。 如果您使用我上面帖子中提到的“静态类初始化器”方式,测试将等到数据库准备好,不是吗?【参考方案2】:您可以编写 Datasource Bean 类集成测试并从 postgresqlContainer 中的参数创建数据源
@Configuration
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Testcontainers
public class TestConfiguration
PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE)
.withInitScript("somepath/init_postgresql.sql")
@Primary
@Bean
public DataSource dataSource()
HikariConfig hikariConfig=new HikariConfig();
hikariConfig.setJdbcUrl(postgres.getUrl());
hikariConfig.setUsername(postgres.getUsername());
hikariConfig.setPassword(postgres.getPassword());
hikariConfig.setDriverClassName(postgres.getDriverClassName());
HikariDataSource hikariDataSource=new HikariDataSource(hikariConfig);
return hikariDataSource;
并将上面的配置类导入到你的springboot测试中,它会自动用TestContainer的Postgres数据源覆盖数据源。
你的测试应该是这样的
@ExtendWith(SpringExtension.clas)
@SpringBootTest(classes = DataSourceConfiguration.class, TestConfiguration.class)
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Testcontainers
public class RepoTest
@Autowired
TestRepository testRepository;
@Test
public void testRepo()
System.out.println("*****************" + testRepository.existsById(1L));
详情请参考此链接: https://medium.com/@arpitsharma14/testcontainer-springboot-tests-faa05b71a7dc
【讨论】:
【参考方案3】:在 SpringBoot 中,您必须使用来自 Postgres-Testcontainer 的 ApplicationContextInitializer 覆盖 DB 参数。测试容器生命周期管理将处理其余部分。应该看起来像这样:
@Testcontainers
@SpringJUnitWebConfig(locations = "classpath:it-context.xml")
@ContextConfiguration(initializers = IntegrationTest.Initializer.class)
public class IntegrationTest
@Container
private static PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer();
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext>
public void initialize(ConfigurableApplicationContext configurableApplicationContext)
TestPropertyValues.of(
"spring.datasource.url=" + postgresqlContainer.getJdbcUrl(),
"spring.datasource.username=" + postgresqlContainer.getUsername(),
"spring.datasource.password=" + postgresqlContainer.getPassword()
).applyTo(configurableApplicationContext.getEnvironment());
@Autowired
private StatusBean status;
@Test
public void appStartedSuccessful()
// app & database successful started and connected
System.out.println(postgresqlContainer.getJdbcUrl());
System.out.println(postgresqlContainer.getTestQueryString());
System.out.println(status.getStartupTimestamp());
【讨论】:
以上是关于使用 Testcontainer 进行 Spring 集成测试 - 数据库在应用程序之后启动的主要内容,如果未能解决你的问题,请参考以下文章
如何运行自定义 docker 镜像 testContainer
在 Spring Boot 集成测试中使用 TestContainer 填充数据库
在 gitlab 管道中执行 testcontainer 集成测试
在 Micronaut 应用程序中使用 TestContainer 的测试环境的 ApplicationContext 不起作用
在 springboottest 中从默认 applicaton.yml 设置 testcontainer 属性的更好方法