在此博客文章中,我想演示如何在Spring Boot测试中集成Testcontainer以便与数据库一起运行集成测试。 我没有使用Testcontainers的Spring Boot模块。 如何与他们合作,我将在另一篇博客文章中进行介绍。 所有示例都可以在GitHub上找到 。
为什么要使用测试容器?
Testcontainers是一个库,可帮助在基于Docker容器的集成测试中集成数据库等基础架构组件。 它有助于避免编写集成测试。 这些是根据另一个系统的正确性通过或失败的测试。 使用Testcontainer,我可以控制这些从属系统。
域介绍
进一步的示例展示了不同的方法,该方法如何通过数据库中不同的存储库实现来保存一些英雄对象,以及相应的测试如何。
package com.github.sparsick.testcontainerspringboot.hero.universum;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Objects;
public class Hero {
private Long id;
private String name;
private String city;
private ComicUniversum universum;
public Hero(String name, String city, ComicUniversum universum) {
this.name = name;
this.city = city;
this.universum = universum;
}
public String getName() {
return name;
}
public String getCity() {
return city;
}
public ComicUniversum getUniversum() {
return universum;
}
}
所有其他存储库都是Spring Boot Web应用程序的一部分。 因此,在本博客文章的结尾,我将演示如何为整个Web应用程序(包括数据库)编写测试。 让我们从一个简单的示例开始,该示例基于JDBC。
基于JDBC测试存储库
假设我们有以下基于JDBC的存储库实现。 我们有两种方法,一种是将英雄添加到数据库中,另一种是从数据库中获取所有英雄。
package com.github.sparsick.testcontainerspringboot.hero.universum;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.util.Collection;
@Repository
public class HeroClassicJDBCRepository {
private final JdbcTemplate jdbcTemplate;
public HeroClassicJDBCRepository(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
public void addHero(Hero hero) {
jdbcTemplate.update("insert into hero (city, name, universum) values (?,?,?)",
hero.getCity(), hero.getName(), hero.getUniversum().name());
}
public Collection
allHeros() {
return jdbcTemplate.query("select * From hero",
(resultSet, i) -> new Hero(resultSet.getString("name"),
resultSet.getString("city"),
ComicUniversum.valueOf(resultSet.getString("universum"))));
}
}
对于此存储库,我们可以编写普通的JUnit5测试,而无需加载Spring应用程序上下文。 因此,首先,我们必须建立对测试库的依赖关系,在这种情况下为JUnit5和Testcontainers。 作为构建工具,我使用Maven。 这两个测试库都提供了所谓的BOM“物料清单” ,这有助于避免我所使用的依赖项中的版本不匹配。 作为数据库,我想使用MySQL。 因此,除了核心模块testcontainers
之外,我还使用了Testcontainers的模块mysql
。 它提供了一个预定义的MySQL容器。 为了简化JUnit5测试代码中专门的容器设置,Testcontainers提供了一个JUnit5模块junit-jupiter
。
<dependencies>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit.jupiter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-bom</artifactId>
<version>${testcontainers.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
现在,我们拥有编写第一个测试的所有内容。
package com.github.sparsick.testcontainerspringboot.hero.universum;
import ...
@Testcontainers
class HeroClassicJDBCRepositoryIT {
@Container
private MySQLContainer database = new MySQLContainer();
private HeroClassicJDBCRepository repositoryUnderTest;
@Test
void testInteractionWithDatabase() {
ScriptUtils.runInitScript(new JdbcDatabaseDelegate(database, ""),"ddl.sql");
repositoryUnderTest = new HeroClassicJDBCRepository(dataSource());
repositoryUnderTest.addHero(new Hero("Batman", "Gotham City", ComicUniversum.DC_COMICS));
Collection<Hero> heroes = repositoryUnderTest.allHeros();
assertThat(heroes).hasSize(1);
}
@NotNull
private DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUrl(database.getJdbcUrl());
dataSource.setUser(database.getUsername());
dataSource.setPassword(database.getPassword());
return dataSource;
}
}
让我们看看如何为测试准备数据库。 首先,我们使用@Testcontainers
注释测试类。 该注释的后面隐藏了Testcontainers提供的JUnit5扩展。 它检查Docker是否安装在计算机上,并在测试期间启动和停止容器。 但是,Testcontainers如何知道应该从哪个容器开始? 在这里,注释@Container
帮助。 它标记了应由Testcontainers扩展管理的容器。 在这种情况下,一个MySQLContainer
由Testcontainers模块提供mysql
。 此类提供了MySQL Docker容器,并处理诸如设置数据库用户,识别何时可以使用数据库等问题。一旦数据库准备就绪可以使用,就必须设置数据库架构。 测试容器也可以在此处提供支持。 ScriptUtils. runInitScript (new JdbcDatabaseDelegate(database,