1.Testcontainers 介绍:
Testcontainers 是一个 Java 库, 它支持 JUnit 测试, 提供公共数据库, Seleniumweb 浏览器或任何可以在 Docker 容器中运行的轻量级, 一次性实例.
测试容器使以下类型的测试更加容易:
数据访问层集成测试:
使用 MySQL,PostgreSQL 或 Oracle 数据库的容器化实例测试您的数据访问层代码, 但无需在开发人员的计算机上进行复杂的设置, 并且测试将始终从已知的数据库状态开始, 避免 "垃圾" 数据的干扰. 也可以使用任何其他可以容器化的数据库类型.
应用程序集成测试:
用于在具有相关性 (例如数据库, 消息队列或 Web 服务器) 的短期测试模式下运行应用程序.
UI / 验收测试:
使用与 Selenium 兼容的容器化 Web 浏览器进行自动化 UI 测试. 每个测试都可以获取浏览器的新实例, 而无需担心浏览器状态, 插件版本或浏览器自动升级. 您将获得每个测试会话或测试失败的视频记录.
更多:
可以签出各种贡献的模块, 或使用 GenericContainer 作为基础创建自己的自定义容器类.
2.Testcontainers 实践示例:
Testcontainers 提供了多种现成的与测试关联的应用程序容器, 如下图:
在本文中, 将演示集成 PostgreSQL 容器和 mockserver 容器的测试.
Testcontainers 必要条件:
1.Docker
2. 支持的 JVM 测试框架: JUnit4,JUnit5,spock...
2.1 集成 PostgreSQL 测试
依赖:
- <dependency>
- <groupId>org.testcontainers</groupId>
- <artifactId>testcontainers</artifactId>
- <version>1.12.5</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.testcontainers</groupId>
- <!-- 指定数据库名称, mysql,mariadb 等等 -->
- <artifactId>PostgreSQL</artifactId>
- <version>1.12.5</version>
- <scope>test</scope>
- </dependency>
配置:
在项目的 src/test/resources/application.properties 文件中配置 PostgreSQL 相关信息
- # 将驱动程序设置为 org.testcontainers.jdbc.ContainerDatabaseDriver, 它是一个 Testcontainers JDBC 代理驱动程序. 初始化数据源时, 此驱动程序将负责启动所需的 Docker 容器.
- spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver
- # 将 JDBC URL 设置为 JDBC:tc:<database image>:<version>:/// 以便 Testcontainers 知道要使用哪个数据库.
- #TC_INITSCRIPT = 指定的数据库初始化的脚本文件位置
- spring.datasource.url=jdbc:tc:PostgreSQL:9.6:///?TC_INITSCRIPT=file:src/main/resources/init_db.sql
- # 将方言明确设置为数据库的方言实现, 否则在启动应用程序时会收到异常. 当您在应用程序中使用 JPA 时(通过 Spring Data JPA), 此步骤是必需的
- spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
测试示例:
为了在 @DataJpaTest 中使用 TC, 您需要确保使用了应用程序定义的 (自动配置的) 数据源. 您可以通过使用 @AutoConfigureTestDatabase 注释测试来轻松完成此操作, 如下所示:
- @RunWith(SpringJUnit4ClassRunner.class)
- @DataJpaTest
- @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
- public class OwnerRepositoryTests {
- @Autowired
- private OwnerRepository ownerRepository;
- @Test
- void findAllReturnsJohnDoe() { // as defined in tc-initscript.sql
- var owners = ownerRepository.findAll();
- assertThat(owners.size()).isOne();
- assertThat(owners.get(0).getFirstName()).isEqualTo("John");
- assertThat(owners.get(0).getLastName()).isEqualTo("Doe");
- }
- }
以上测试将使用 Testcontainers 提供的 PostgreSQL 容器进行测试, 从而排除了外部环境对测试的干扰.
当需要用本地数据库进行集成测试时, 我们只要使用 @SpringBootTest 替换如上两个注解即可:
- @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
- @AutoConfigureMockMvc
- public class OwnerResourceTests {
- @Autowired
- WebApplicationContext wac;
- @Test
- void findAllReturnsJohnDoe() throws Exception {
- given()
- .webAppContextSetup(wac)
- .when()
- .get("/owners")
- .then()
- .status(HttpStatus.OK)
- .body(
- "_embedded.owners.firstName", containsInAnyOrder("John"),
- "_embedded.owners.lastName", containsInAnyOrder("Doe")
- );
- }
- }
以上测试将使用真实运行环境的数据库进行测试.
2.2 集成 mockServer 测试
Mock Server 可用于通过将请求与用户定义的期望进行匹配来模拟 HTTP 服务.
依赖:
- <dependency>
- <groupId>org.testcontainers</groupId>
- <artifactId>mockserver</artifactId>
- <version>1.12.5</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mock-server</groupId>
- <artifactId>mockserver-netty</artifactId>
- <version>5.5.4</version>
- </dependency>
- <dependency>
- <groupId>org.mock-server</groupId>
- <artifactId>mockserver-client-java</artifactId>
- <version>5.5.4</version>
- </dependency>
测试示例:
- // 创建一个 MockServer 容器
- @Rule
- public MockServerContainer mockServer = new MockServerContainer();
以及使用 Java MockServerClient 设置简单的期望.
- new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort())
- .when(request()
- .withPath("/person")
- .withQueryStringParameter("name", "peter"))
- .respond(response()
- .withBody("Peter the person!"));
- //... 当一个 get 请求至'/person?name=peter' 时会返回 "Peter the person!"
测试(使用 restassured 进行测试):
- RestAssured.baseURI = "http://" + mockServer.getContainerIpAddress();
- RestAssured.port = mockServer.getServerPort();
- given().queryParam("name", "peter")
- .get("/person")
- .then()
- .statusCode(HttpStatus.OK.value())
- .body(is("Peter the person!"));
完整代码如下:
- @RunWith(SpringJUnit4ClassRunner.class)
- public class OneTests {
- @Rule
- public MockServerContainer mockServer = new MockServerContainer();
- @Test
- public void v() {
- RestAssured.baseURI = "http://" + mockServer.getContainerIpAddress();
- RestAssured.port = mockServer.getServerPort();
- new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort())
- .when(request()
- .withPath("/person")
- .withQueryStringParameter("name", "peter"))
- .respond(response()
- .withBody("Peter the person!"));
- given().queryParam("name", "peter")
- .get("/person")
- .then()
- .statusCode(HttpStatus.OK.value())
- .body(is("Peter the person!"));
- }
- }
3. 总结:
Testcontainers 轻松的解决了集成测试时测试代码与本地组件耦合, 从而出现各种意外失败的问题(比如本地数据库中存在脏数据影响到了集成测试, 多个集成测试同时运行时相互干扰导致测试结果意外失败). 笔者之前专门为集成测试准备了一套数据库, 使数据和其他环境隔离掉, 但还是会遇到多个集成测试一起跑相互干扰的问题, Testcontainers 轻松的解决了笔者的问题.
关注笔者公众号, 推送各类原创 / 优质技术文章
来源: https://www.cnblogs.com/dongxishaonian/p/12653200.html