java单元/集成测试中使用Testcontainers

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介:

java单元/集成测试中使用Testcontainers

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测试#
依赖:#
Copy

<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.12.5</version>
<scope>test</scope>


<groupId>org.testcontainers</groupId>
  <!--指定数据库名称,mysql,mariadb等等-->
<artifactId>postgresql</artifactId>
<version>1.12.5</version>
<scope>test</scope>


配置:#
在项目的src/test/resources/application.properties文件中配置postgresql相关信息

Copy

将驱动程序设置为org.testcontainers.jdbc.ContainerDatabaseDriver,它是一个Testcontainers JDBC代理驱动程序。初始化数据源时,此驱动程序将负责启动所需的Docker容器。

spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver

将JDBC URL设置为JDBC:tc:::///以便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注释测试来轻松完成此操作,如下所示:

Copy
@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替换如上两个注解即可:

Copy
@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服务。

依赖:#
Copy

<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>

测试示例:#
Copy
//创建一个MockServer容器
@Rule
public MockServerContainer mockServer = new MockServerContainer();
以及使用Java MockServerClient设置简单的期望。

Copy
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进行测试):

Copy
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!"));

完整代码如下:

Copy
@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

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
3天前
|
Java 测试技术 数据库
【JAVA基础篇教学】第十七篇:Java单元测试
【JAVA基础篇教学】第十七篇:Java单元测试
|
5天前
|
Web App开发 JavaScript 前端开发
《手把手教你》系列技巧篇(三十九)-java+ selenium自动化测试-JavaScript的调用执行-上篇(详解教程)
【5月更文挑战第3天】本文介绍了如何在Web自动化测试中使用JavaScript执行器(JavascriptExecutor)来完成Selenium API无法处理的任务。首先,需要将WebDriver转换为JavascriptExecutor对象,然后通过executeScript方法执行JavaScript代码。示例用法包括设置JS代码字符串并调用executeScript。文章提供了两个实战场景:一是当时间插件限制输入时,用JS去除元素的readonly属性;二是处理需滚动才能显示的元素,利用JS滚动页面。还给出了一个滚动到底部的代码示例,并提供了详细步骤和解释。
28 10
|
13天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(三十六)-java+ selenium自动化测试-单选和多选按钮操作-番外篇(详解教程)
【4月更文挑战第28天】本文简要介绍了自动化测试的实战应用,通过一个在线问卷调查(&lt;https://www.sojump.com/m/2792226.aspx/&gt;)为例,展示了如何遍历并点击问卷中的选项。测试思路包括找到单选和多选按钮的共性以定位元素,然后使用for循环进行点击操作。代码设计方面,提供了Java+Selenium的示例代码,通过WebDriver实现自动答题。运行代码后,可以看到控制台输出和浏览器的相应动作。文章最后做了简单的小结,强调了本次实践是对之前单选多选操作的巩固。
24 0
|
2天前
|
前端开发 Java 测试技术
《手把手教你》系列技巧篇(四十二)-java+ selenium自动化测试 - 处理iframe -下篇(详解教程)
【5月更文挑战第6天】本文介绍了如何使用Selenium处理含有iframe的网页。作者首先解释了iframe是什么,即HTML中的一个框架,用于在一个页面中嵌入另一个页面。接着,通过一个实战例子展示了在QQ邮箱登录页面中,由于输入框存在于iframe内,导致直接定位元素失败。作者提供了三种方法来处理这种情况:1)通过id或name属性切换到iframe;2)使用webElement对象切换;3)通过索引切换。最后,给出了相应的Java代码示例,并提醒读者根据iframe的实际情况选择合适的方法进行切换和元素定位。
7 0
|
3天前
|
前端开发 测试技术 Python
《手把手教你》系列技巧篇(四十一)-java+ selenium自动化测试 - 处理iframe -上篇(详解教程)
【5月更文挑战第5天】本文介绍了HTML中的`iframe`标签,它用于在网页中嵌套其他网页。`iframe`常用于加载外部内容或网站的某个部分,以实现页面美观。文章还讲述了使用Selenium自动化测试时如何处理`iframe`,通过`switchTo().frame()`方法进入`iframe`,完成相应操作,然后使用`switchTo().defaultContent()`返回主窗口。此外,文章提供了一个包含`iframe`的HTML代码示例,并给出了一个简单的自动化测试代码实战,演示了如何在`iframe`中输入文本。
14 3
|
4天前
|
JavaScript 前端开发 Java
《手把手教你》系列技巧篇(四十)-java+ selenium自动化测试-JavaScript的调用执行-下篇(详解教程)
【5月更文挑战第4天】本文介绍了如何使用JavaScriptExecutor在自动化测试中实现元素高亮显示。通过创建并执行JS代码,可以改变元素的样式,例如设置背景色和边框,以突出显示被操作的元素。文中提供了一个Java示例,展示了如何在Selenium中使用此方法,并附有代码截图和运行效果展示。该技术有助于跟踪和理解测试过程中的元素交互。
8 0
|
5天前
|
Java 测试技术 持续交付
Java测试驱动开发实践指南
Java测试驱动开发实践指南
12 0
|
6天前
|
前端开发 JavaScript Java
java测试链接超时返回前端
java测试链接超时返回前端
16 0
|
6天前
|
JavaScript 前端开发 测试技术
《手把手教你》系列技巧篇(三十八)-java+ selenium自动化测试-日历时间控件-下篇(详解教程)
【5月更文挑战第2天】在自动化测试过程中,经常会遇到处理日期控件的点击问题。宏哥之前分享过一种方法,但如果输入框是`readonly`属性,这种方法就无法奏效了。不过,通过修改元素属性,依然可以实现自动化填写日期。首先,定位到日期输入框并移除`readonly`属性,然后使用`sendKeys`方法输入日期。这样,即使输入框设置了`readonly`,也能成功处理日期控件。
24 1
|
7天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(三十七)-java+ selenium自动化测试-日历时间控件-上篇(详解教程)
【5月更文挑战第1天】该文介绍了使用Selenium自动化测试网页日历控件的方法。首先,文章提到在某些Web应用中,日历控件常用于选择日期并筛选数据。接着,它提供了两个实现思路:一是将日历视为文本输入框,直接输入日期;二是模拟用户交互,逐步选择日期。文中给出了JQueryUI网站的一个示例,并展示了对应的Java代码实现,包括点击日历、选择日期等操作。
30 0