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轻松的解决了笔者的问题。


关注笔者公众号,推送各类原创/优质技术文章 ⬇️

java单元/集成测试中使用Testcontainers的更多相关文章

  1. Java 在Excel单元格中应用一种/多种字体样式

    在Excel表格中,设置单元格字体样式时,可以对单元格内的所有字符应用同一样式,即获取指定单元,应用样式即可:另外也可以对单元格内的不同字符内容应用不同字体样式,即获取单元格中的字符位置,应用样式:本 ...

  2. 基于spring-boot的应用程序的单元+集成测试方案

    目录 概述 概念解析 单元测试和集成测试 Mock和Stub 技术实现 单元测试 测试常规的bean 测试Controller 测试持久层 集成测试 从Controller开始测试 从中间层开始测试 ...

  3. Java读取word中表格

    因为要新建一个站,公司要把word表格的部分行列存到数据库中.之前用java操作过excel,本来打算用java从word表格中读取数据,再存到数据库中,结果因为权限不够,无法访问公司要写的那个数据库 ...

  4. 在 Java 应用程序中绑定 Bean 和数据

    本指南介绍了 NetBeans IDE 对 Java 应用程序中 Bean 绑定和数据绑定的支持. 要学完本教程,您需要具备以下软件和资源. 软件或资源 要求的版本 NetBeans IDE 版本 7 ...

  5. JAVA、android中常用的一些jar包的作用

    正文: 这里主要介绍的是hibernate使用到的.jar Hibernate一共包括了23个jar包,令人眼花缭乱.本文将详细讲解Hibernate每个jar包的作用,便于你在应用中根据自己的需要进 ...

  6. Java基础学习总结(24)——Java单元测试之JUnit4详解

    Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @BeforeClass 全局只会执行一次,而且是第一个运行 @Before  ...

  7. Java单元測试工具JUnit 5新特性一览

    Java单元測试工具JUnit 5新特性一览 作者:chszs,未经博主同意不得转载. 经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs JUnit是最流行的开源 ...

  8. Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析

    目录 0.前言 1.TemporalAccessor源码 2.Temporal源码 3.TemporalAdjuster源码 4.ChronoLocalDate源码 5.LocalDate源码 6.总 ...

  9. Java处理Excel中的日期格式

    Java处理Excel中的日期格式 2011-12-23 17:34:03|  分类: java |举报 |字号 订阅 下载LOFTER 我的照片书  |   在Excel中的日期格式,其数值为距离1 ...

随机推荐

  1. 【开发技巧】再见,BLE的那些坑!

    蓝牙,平常你用的多吗?上班路上戴着蓝牙耳机听音乐.看视频打开蓝牙分享个人热点给小伙伴们解锁共享单车时,打开蓝牙就能迅速解锁...... BLE-蓝牙低能耗技术,方便了我们的生活,但是开发者在开发过程中 ...

  2. JZOJ 3928. 【NOIP2014模拟11.6】射击

    3928. [NOIP2014模拟11.6]射击 (Standard IO) Time Limits: 1000 ms Memory Limits: 65536 KB Description 有问题, ...

  3. openwrt 外挂usb 网卡 RTL8188CU 及添加 RT5572 kernel支持

    RT5572 原来叫 Ralink雷凌 现在被 MTK 收购了,淘宝上买的很便宜50块邮,2.4 5G 双频.在 win10 上插了试试,果然是支持 5G.这上面写着 飞荣 是什么牌子,有知道的和我说 ...

  4. 计算机网络原理实验_使用网络协议分析仪Wireshark

    一.实验名称  使用网络协议分析仪Wireshark 二.实验目的: 1. 掌握安装和配置网络协议分析仪Wireshark的方法: 2. 熟悉使用Wireshark工具分析网络协议的基本方法,加深对协 ...

  5. Java实现GBK转码到UTF-8(文件)

    所以,虚无的悲叹,寻根问底仍是由于肉身的圈定.肉身蒙蔽了灵魂的眼睛,单是看见要回那无中去,却忘了你原是从那无中来.                                            ...

  6. 自定义添加$_SERVER中的变量

    如何根据自己项目中的需求自定义$_SERVER中的变量呢?比如$_SERVER['XU_TEXT_PARAM'],这个超全局变量输出肯定是会报错的.那如何自定义它,让它输出我们想要输出的值呢? 1.在 ...

  7. Python基础数据类型2

    lst.extend([1,2,3]) # 扩展 --- 迭代添加 整型和布尔值不能迭代print(lst) lst1 = [1,2,3]lst2 = [4,5,6]lst3 = lst1 + lst ...

  8. tkinter学习1

    GUI 用户交互界面 tkinter 介绍 tkinter是 python自带的gui库,对图像处理库tk的封装 #导入tkinter库 import tkinter #创建主窗口对象 root = ...

  9. linux相关的帮助文档

    几乎所有linux发行版都会提供大量的有用的文档. 手册页 linux发行版包含了有关常用命令.系统调用和库函数的手册页.手册页被分成不同的章节并分别标以序号: Section      名称     ...

  10. [BUG]Uncaught TypeError: Illegal invocation at HTMLDivElement.

    <div id="clickMe" key="1">点我</div> clickMe.onclick = function (event ...