2天时间,聊今年最热的 Agent、上下文工程、AI 产品创新等话题。2025 年最后一场~ 了解详情
写点什么

Maven 实战(五)——自动化 Web 应用集成测试

  • 2011-03-13
  • 本文字数:4677 字

    阅读完需:约 15 分钟

自动化集成测试的角色

本专栏的上一篇文章讲述了 Maven 与持续集成的一些关系及具体实践,我们都知道,自动化测试是持续集成必不可少的一部分,基本上,没有自动化测试的持续集成,都很难称之为真正的持续集成。我们希望持续集成能够尽早的暴露问题,但这远非配置一个 Hudson/Jenkins 服务器那么简单,只有真正用心编写了较为完整的测试用例,并一直维护它们,持续集成才能孜孜不倦地运行测试并第一时间报告问题。

自动化测试这个话题很大,本文不想争论测试先行还是后行,这里强调的是测试的自动化,并基于具体的技术(Maven、 JUnit、Jetty 等)来介绍一种切实可行的自动化 Web 应用集成测试方案。当然,自动化测试还包括单元测试、验收测试、性能测试等,在不同的场景下,它们都能为软件开发带来极大的价值。本文仅限于讨论集成测试,主要是因为笔者觉得这是一个非常重要却常常被忽略的实践。

基于 Maven 的一般流程

集成测试与单元测试最大的区别是它需要尽可能的测试整个功能及相关环境,对于测试 Web 应用而言,通常有这么几步:

  1. 启动 Web 容器
  2. 部署待测试 Web 应用
  3. 以 Web 客户端的角色运行测试用例
  4. 停止 Web 容器

启动 Web 容器可以有很多方式,例如你可以通过 Web 容器提供的 API 采用编程的方式来启动容器,但在 Maven 的环境下,配置插件显得更简单。如果你了解 Maven 的生命周期模型,就可能会想到,我们可以在 pre-integration-test 阶段启动容器,部署待测试应用,然后在 integration-test 阶段运行集成测试用例,最后在 post-integrate-test 阶段停止容器。也就是说,对于步骤 1,2 和 4 我们只须进行一些简单的配置,不必编写额外的代码。第 3 步是以黑盒的形式模拟客户端进行测试,需要注意的是,这里通常要求你理解一些基本的 HTTP 协议知识,例如服务端在什么情况下应该返回 HTTP 代码 200,什么时候应该返回 401 错误,以及所支持的 Content-Type 是什么等等。

至于测试用例该怎么写,除了需要用到一些用来访问 Web 以及解析响应详细的基础设施工具类之外,其他内容与单元测试大同小异,基本就是准备测试数据、访问服务、验证返回值等等。

一个简单的例子

谈了不少理论,现在该给个具体的例子了,譬如现在有个简单的 Servlet,它接受参数 a 和 b,做加法后返回二者之和,如果参数不完整,则返回 HTTP 400 错误,表示客户端的请求有问题。

复制代码
public class AddServlet
extends HttpServlet
{
@Override
protected void doGet( HttpServletRequest req, HttpServletResponse resp )
throws ServletException,
IOException
{
String a = req.getParameter( "a" );
String b = req.getParameter( "b" );
if ( a == null || b == null )
{
resp.setStatus( 400 );
return;
}
int result = Integer.parseInt( a ) + Integer.parseInt( b );
resp.setStatus( 200 );
resp.getWriter().print( result );
}
}

为了测试这段代码,我们需要一个 Web 容器,这里暂且使用 Jetty,因为目前来说它与 Maven 集成的相对最好。Jetty 提供了一个 Jetty Maven Plugin ,借助该插件,我们可以随时启动 Jetty 并部署 Maven 默认目录布局的 Web 项目,实现快速开发和测试。这里我们需要的是在 pre-integration-test 阶段启动 Jetty,在 post-integrate-test 阶段停止容器,对应的 POM 配置如下:

复制代码
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.3.0.v20110203</version>
<configuration>
<stopPort>9966</stopPort>
<stopKey>stop-jetty-for-it</stopKey>
</configuration>
<executions>
<execution>
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<daemon>true</daemon>
</configuration>
</execution>
<execution>
<id>stop-jetty</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>

XML 代码中第一处 configuration 是插件的全局配置,stopPort 和 stopKey 是该插件用来停止 Jetty 需要用到的 TCP 端口及消息关键字。接着是两个 executation 元素,第一个 executation 将 jetty-maven-plugin 的 run 目标绑定至 Maven 的 pre-integration-test 生命周期阶段,表示启动容器,第二个 executation 将 stop 目标绑定至 post-integration-test 生命周期阶段,表示停止容器。需要注意的是,启动 Jetty 时我们需要配置 deamon 为 true,让 Jetty 在后台运行以免阻塞 mvn 命令。此外,jetty-maven-plugin 的 run 目标也会自动部署当前 Web 项目。

准备好 Web 容器环境之后,我们接着看一下测试用例代码:

复制代码
public class AddServletIT
{
@Test
public void addWithParametersAndSucceed()
throws Exception
{
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet( "http://localhost:8080/add?a=1&b=2" );
HttpResponse response = httpclient.execute( httpGet );
Assert.assertEquals( 200, response.getStatusLine().getStatusCode() );
Assert.assertEquals( "3", EntityUtils.toString( response.getEntity() ) );
}
@Test
public void addWithoutParameterAndFail()
throws Exception
{
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet( "http://localhost:8080/add" );
HttpResponse response = httpclient.execute( httpGet );
Assert.assertEquals( 400, response.getStatusLine().getStatusCode() );
}
}

为了能够访问应用,这里用到了 HttpClient ,两个测试方法都初始化一个 HttpClient,然后创建 HttpGet 对象用来访问 Web 地址。第一个测试方法顾名思义用来测试成功的场景,它提供参数 a=1 和 b=2,执行请求后,验证返回结果成功(HTTP 状态码 200)并且内容为正确的值 3。第二个测试方法则用来测试失败的场景,当不提供参数的时候,服务器应该返回一个 HTTP 400 错误。该测试类其实是相当粗糙的,例如有硬编码的服务器 URL,这里的目的仅仅是通过尽可能简单的代码来展现一个自动化集成测试的实现过程。

上述代码中,测试类的名称为 AddServletIT,而不是一般的 **Test,IT 表示 IntegrationTest,这么命名是为了和单元测试区分开来,这样,鉴于 Maven 默认的测试命名约定,Maven 在 test 生命周期阶段执行单元测试时,就不会涉及集成测试。现在,我们希望 Maven 在 integration-test 阶段执行所有以 IT 结尾命名的测试类,配置 Maven Surefire Plugin 如下:

复制代码
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7.2</version>
<executions>
<execution>
<id>run-integration-test</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>
<include>**/*IT.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>

通过命名规则和插件配置,我们优雅地分离了单元测试和集成测试,而且我们知道在 integration-test 阶段,Jetty 容器已经启动完成了。如果你在使用 TestNG,那你还可以使用其测试组的特性来分离单元测试和集成测试,Maven Surefire Plugin 对其也有着很好的支持

一切就绪了,运行 mvn clean install 以自动运行集成测试,我们可以看到如下的输出片段:

复制代码
[INFO] --- jetty-maven-plugin:7.3.0.v20110203:run (start-jetty) @ webapp-demo ---
[INFO] Configuring Jetty for project: webapp-demo
[INFO] webAppSourceDirectory /home/juven/git_juven/webapp-demo/src/main/webapp does not exist. Defaulting to /home/juven/git_juven/webapp-demo/src/main/webapp
[INFO] Reload Mechanic: automatic
[INFO] Classes = /home/juven/git_juven/webapp-demo/target/classes
[INFO] Context path = /
...
2011-03-06 14:55:15.676:INFO::Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server
[INFO]
[INFO] --- maven-surefire-plugin:2.7.2:test (run-integration-test) @ webapp-demo ---
[INFO] Surefire report directory: /home/juven/git_juven/webapp-demo/target/surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.juvenxu.webapp.demo.AddServletIT
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.344 sec
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] --- jetty-maven-plugin:7.3.0.v20110203:stop (stop-jetty) @ webapp-demo ---

可以看到 jetty-maven-plugin:7.3.0.v20110203:run 对应了 start-jetty,maven-surefire- plugin:2.7.2:test 对应了 run-integration-test,jetty-maven- plugin:7.3.0.v20110203:stop 对应了 stop-jetty,与我们的配置和期望完全一致。此外两个测试也都成功了!

小结

相对于单元测试来说,集成测试更难编写,因为需要准备更多的环境,本文只涉及了Web 容器最简单的情形,实际的开发情形中,你可能会遇到数据库,第三方Web 服务,更复杂的容器配置和数据格式等等,这都使得编写集成测试变得让人畏惧。然而反过来考虑,无论如何你都需要测试,虽然这个自动化过程的投入很大,但收益往往更加客观,这不仅仅是手动测试时间的节省,更重要的是,你无法保证手动测试能被高频率的反复执行,也就无法保证问题能被尽早暴露。

对于Web 应用来说,编写集成测试有助于你考虑和设计Web 应用对外暴露的接口,这种“开发实现”/“测试审察”之间的角色转换往往能造就更清晰的设计,这也是编写测试最大的好处之一。

Maven 用户能够得益于 Maven 的插件系统,不仅能节省大量的编码,还能得到稳定的工具,Jetty Maven Plugin 和 Maven Surefire Plugin 就是最好的例子。本文只涉及了 Jetty,如果读者的环境是 Tomcat 或者 JBoss 等其他容器,则需要查阅相关的文档以得到具体的实现细节,你可能对 Tomcat Maven Plugin JBoss Maven Plugin 、或者 Cargo Maven2 Plugin 感兴趣。

关于作者

许晓斌(Juven Xu),国内社区公认的 Maven 技术专家、Maven 中文用户组创始人、Maven 技术的先驱和积极推动者。对 Maven 有深刻的认识,实战经验丰富,不仅撰写了大量关于 Maven 的技术文章,而且还翻译了开源书籍《Maven 权威指南》,对 Maven 技术在国内的普及和发展做出了很大的贡献。就职于 Maven 之父的公司,负责维护 Maven 中央仓库,是 Maven 仓库管理器 Nexus(著名开源软件)的核心开发者之一,曾多次受邀到淘宝等大型企业开展 Maven 方面的培训。此外,他还是开源技术的积极倡导者和推动者,擅长 Java 开发和敏捷开发实践。他的个人网站是: http://www.juvenxu.com

2011-03-13 00:1518911

评论

发布
暂无评论
发现更多内容

Python读execl之xlrd库函数详解二:单元格相关

Python Excel 数据读取

精选案例 |《金融电子化》:光大银行云原生背景下的运维监控体系建设

博睿数据

云原生 可观测性 智能运维 博睿数据 精选案例

WebUI自动化环境搭建

Python 自动化测试 selenium

火山引擎入选《2022爱分析 · DataOps厂商全景报告》,旗下DataLeap产品能力获认可

字节跳动数据平台

大数据 云服务 数据产品

优质的云管平台厂商重点推荐-行云管家

行云管家

云计算 云管平台 行云管家

Selenium WebDriver API 学习笔记(三):浏览器控制

Python 自动化测试 selenium

公司项目引入这种方式,开发应用又快又准

引迈信息

项目管理 程序员 敏捷开发 低代码

Serverless Streaming:毫秒级流式大文件处理探秘

华为云开发者联盟

云计算 大数据 华为云 企业号 2 月 PK 榜 华为云开发者联盟

低代码实现探索(五十六)低代码正确方式

零道云-混合式低代码平台

Python读execl之xlrd库函数详解三:行、列相关

Python Excel 数据读取

Python+Opencv解析一段视频并逐帧保存到本地

Python 数据读取 摄像头

成都市信息安全等级保护测评机构详细名单汇总

行云管家

成都 等保 等级保护 等保测评

压电石英晶体谐振器,国产替代需求强劲

华秋电子

面试官:熔断和降级有什么区别?

小小怪下士

Java 后端 熔断

Java高手速成 | 图说重定向与转发

TiAmo

Java 重定向

详解 APISIX Lua 动态调试插件 inspect

API7.ai 技术团队

插件 api 网关 APISIX

世界上最健康的程序员作息表!「值得一看」

王中阳Go

golang 高效工作 学习方法 程序员 作息时间

普通单双面板的生产工艺流程:图形转移

华秋电子

Outcome VS. Output:研发效能提升中,谁会更胜一筹?

LigaAI

敏捷开发 研发管理 技术管理 产品管理 企业号 2 月 PK 榜

Zebec完成BNB Chain以及Near链上协议部署,多链化进程加速

西柚子

小白指南:手把手教你用低代码开发一个应用页面

HarmonyOS开发者

HarmonyOS

从“服务”,到“赋能”,日日顺再次定义供应链生态建设

联营汇聚

Selenium WebDriver API 学习笔记(一):元素定位

Python 自动化测试 selenium

组装式专家洞察|中国移动初瑞:基于智慧中台的“组装式”探索实践

信通院IOMM数字化转型团队

组装式应用 组装式创新 IOMM

SVFormer:走进半监督动作识别的视觉 Transformer

Zilliz

计算机视觉 Transformer

应用部署初探:6个保障安全的最佳实践

SEAL安全

应用部署 企业号 2 月 PK 榜 安全部署

Selenium WebDriver API 学习笔记(二):浏览器控制

Python 自动化测试 selenium

什么是BOM?与焊盘不匹配,怎么办?

华秋电子

中国一级市场5年完成1039个投融资事件;红杉中国、启明创投、高瓴创投在2022年最为活跃-创业邦发布《2022年合成生物学产业投资报告》

创业邦

Maven实战(五)——自动化Web应用集成测试_Java_许晓斌_InfoQ精选文章