10 月 23 - 25 日,QCon 上海站即将召开,现在大会已开始正式报名,可以享受 8 折优惠 了解详情
写点什么

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

评论

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

云原生平台,让边缘应用玩出花!

天翼云开发者社区

CDN 云平台

超实用转型攻略!《2022央国企云原生落地实用指南》重磅发布(附下载链接)

York

云原生 系统架构 数字化转型 信创 国资委中央企业

数据治理笔记

老猎人

IT运维管理指什么?如何建立有效的IT运维管理系统?

行云管家

运维 IT运维 服务器运维

可观测性提升软件工程质量,观测云出席2022 QECon全球软件质量&效能大会

观测云

【字体反爬】猫X眼YingShi,我们又来欺负你了,用到了 OCR 识别技术

梦想橡皮擦

Python 爬虫 7月月更

云生态大会,随“峰”而来!

天翼云开发者社区

2022第十四届南京国际智慧城市|物联网|大数据博览会

AIOTE智博会

南京智博会 物联网展览会 智慧城市展览会

人工智能展会|2022第十四届南京国际人工智能展览会

AIOTE智博会

人工智能展览会

百度工程师眼中的云原生可观测性追踪技术

百度Geek说

Java 云原生

CentOS 8里的这个功能,天翼云SFS弹性文件校准了

天翼云开发者社区

Centos 7 CentOS 8 弹性文件

瓜分30万奖金!DeepRec CTR模型性能优化天池挑战赛来啦

阿里云大数据AI技术

深度学习

精品方案|海泰云密码应用服务解决方案 打造安全合规的云上应用

电子信息发烧客

AntDB数据库产品入选中国信通院《全球数据库产业图谱(2022)》

亚信AntDB数据库

国产数据库

从0到1 拿下C语言—程序结构及使用示例

一碗黄豆酱

重磅预告!易观分析联合微软、中央财经大学,共话数字经济

易观分析

数据驱动

阿里云发布《升舱-数据仓库升级交付标准化》白皮书

Lily

最右×微帧,高质量的HEIF图片编码压缩技术

微帧Visionular

计算机视觉 HEIF 视频编解码 图片压缩 WebP

IReport导出PDF字体加粗失效

源字节1号

软件开发 前端开发 后端开发 小程序开发

2022第十五届南京国际工业自动化及工业机器人展览会

AIOTE智博会

【等保常见问题解答】等保测评机构能帮忙做等保整改吗?

行云管家

网络安全 等保 等保测评 等保整改

智慧灯杆展会|2022南京国际智慧灯杆及智慧路灯展览会

AIOTE智博会

2022第十四届南京国际智慧工地展览会|智慧工地展

AIOTE智博会

智慧工地展览会

大咖说·图书分享 | HaaS 物联网设备云端一体开发框架

大咖说

阿里巴巴 物联网 大咖 HaaS

兼容认证|青云QKCP与观测云完成产品兼容性互认证,携手打造云原生可观测能力

观测云

iOS中内存管理(ARC)

NewBoy

ios 前端 移动端 iOS 知识体系 7月月更

绿色低碳天翼云,数字经济新引擎!

天翼云开发者社区

云计算 大数据 AI 数字化转型

C#/VB.NET在 Word 中插入水印

Geek_249eec

C# word 添加水印 VB.NET

开源demo| ARCall 小程序开源示例发布

anyRTC开发者

小程序 音视频 视频通话 开源demo ARCall

nacos注册中心之服务地址动态感知

急需上岸的小谢

7月月更

设计微服务安全架构

Damon

7月月更

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