写点什么

用 Spring 实现非端到端验收测试

  • 2013-12-30
  • 本文字数:3478 字

    阅读完需:约 11 分钟

验收测试让交付团队超越了基本的持续集成,即验证应用程序是否为用户提供了有价值的功能。不过对于刚开始尝试部署流水线的团队来说,想要自动化验收测试,需要跨过三大门槛。

一是实现和维护验收测试的技术门槛。理想情况下,验收测试最好可以模拟用户与应用程序的真实交互,因此如果有图形界面的话,验收测试理应通过这个界面和系统打交道。然而,直接通过 GUI 进行测试会遇到几个问题:界面变化速度很快、场景的准备相对复杂、拿到测试结果较难等。比如一个典型的 WEB 应用程序,如果通过 GUI 测试,那么一般需要解析 HTML 标签来填写参数,提交表单,最后再次通过解析来获取系统的返回值。如果测试代码中充斥着操作 HTML 的细节,测试的可读性就会大大下降,验收测试本身也更脆弱,在需求变更时反而会拖慢进度。

二是交付团队工作方式的变化。在传统团队中,需求分析、开发和测试是独立而又顺序的过程。就算能形成详细的需求文档,三方对同一段文字可能都有自己的理解。结果经常出现偏差,需求分析人员抱怨开发人员没有正确理解需求文档,开发人员抱怨需求文档不清晰、抱怨测试人员故意挑刺。敏捷实践和验收测试的出现缓解了这一问题,通过预先定义验收规格,减少文字上的误解,明确了开发工作的完成标准。不过这种思维方式的转变很难一蹴而就,需要交付团队及其利益关系人共同持续努力才能成功。

三是对组织的环境、配置管理及部署流程的挑战。当引入自动化验收测试后,对整个部署流水线的自动化程度会有更高要求。比如部署流水线应该能够自动将应用程序部署到待测试的环境中。如果应用程序依赖数据库,那么还应该能够部署数据库 schema。另外一些运行时配置也需要通过脚本完成设置。这当中除了脚本准备之外,组织的环境管理也是要能跟上的。一般情况下,稍微大一些的组织都是有专门的运维团队(而非交付团队)来管理硬件设备和其配置的。因此,这个问题一般也涉及多个团队来协作解决。

面对这三座大山和进度压力,新手团队可能会感慨“信息量略大”而止步不前。这时不妨考虑各个击破,三个问题中的工作方式转变涉及的利益干系人最多,难度也最大;环境管理问题虽然涉及不同团队,但一般还是技术部门内的问题,关起门来好商量;验收测试的实现 / 维护主要是技术问题,相对最简单。如果时间和资源确实有限,不妨考虑牺牲一部分验收测试的有效性,采用简单的非端到端验收测试,在自动化部署流程方面也可以做一些折中,集中力量转变工作方式。当整个工作已经进入节奏,再去改进某个具体环节时就顺利很多了。团队只要愿意迈出一小步,也能获得很大的价值。

过渡方案:相对简单的非端到端验收测试

如果团队的技术积累还不足,又没有足够的资源,不妨考虑简单一些的验收测试策略作为过渡方案。非端到端的验收测试是指直接调用应用程序内部的逻辑结构来驱动测试。由于测试代码和产品代码都使用同一种语言编写,可以省去比较繁琐的数据格式解析。而在准备测试数据和场景时,直接调用内部逻辑块一般也更方便。以典型的使用 SpringFramework 的 Java WEB 应用程序为例,团队可以采用和集成测试类似的基础架构来编写非端到端的验收测试。

这里所说的集成测试的目的是验证应用程序与外部服务的连接能否正常工作。这与应用程序实现的具体功能关系不大,因此一般只加载必需的 ApplicationContext。

图表 1 集成测试

非端到端的验收测试可以采用和集成测试一样的测试基础架构,这样你就可以使用熟悉的测试库了,不同的是需要加载整个 ApplicationContext 以尽可能模拟应用程序被部署后的情况。

图表 2 加载整个上下文

由于可以访问整个 ApplicationContext 中的任一对象,我们可以通过访问应用程序的内部组件来执行测试,比如应用层的某个 Service。但需要注意的是,选取的组件离 UI 层越远,其模拟真实用户交互的有效性就越差,而且受内部实现变更的影响越大。如果应用程序使用 spring-webmvc 的 3.2 以上版本,推荐使用它的 mvc 测试库。spring-test-mvc 提供了类似 http 请求的 DSL,此时虽然测试还是基于 ApplicationContext,但并不直接访问内部组件了。这个方案对于新手团队比较友善,但请注意,这仅仅是个过渡方案,因为:

  • 非端到端测试无法提供全面的回归测试,尤其是 UI 操作。在好几个项目中,我们发现仅采用非端到端测试覆盖的功能,团队不得不保留手工回归测试。如果 UI 上包含了大量复杂的控制逻辑甚至有业务逻辑泄漏到 UI 组件中,这会稀释验收测试带来的收益。
  • 由于测试加载的 ApplicationContext 和 Web 容器加载的 ApplicationContext 存在差异,非端到端测试可能会漏掉一些问题。比如在非端到端测试中一次性加载了 booking-servlet.xml 和 root.xml,使他们成为了一个整体的上下文,而实际上在 Web 容器中并不完全是这样,root.xml 中的 bean 并不能访问和控制 booking-servlet.xml 中的 bean。一个常见问题就是如果在 booking-servlet.xml 中需要使用占位符,而恰巧我们已经在 root.xml 中有一个现成的 context:placeholder/ ,看起来水到渠成,而且在测试中也没有问题,但实际部署到 web 容器时,就会加载失败。
  • 非端到端的验收测试不能作为任务完成的最终标准。因为还有 UI 部分还没有完成。当这类验收测试通过时,我把这个任务称作“可以进入 UI 调试的”。

因此,如果团队有足够的技能和资源时还是应该直接使用端到端的验收测试,尤其当应用程序提供 API(比如 WebService)或是采用更易于解析的数据格式与客户端交互时。比如如果应用程序提供了基于 JSON 的 API,完全可以使用 http-client 来驱动测试。

实现非端到端的验收测试

来看看第一个验收测试,这个案例来自于著名的 dddsample ,为了让验收测试能够看上去高端大气上档次,我们将使用 Cucumber 来组织验收测试。验收场景描述的是业务员如何登记航运货件并解释了登记完成后货件的各项状态。

图表 3 第一个用户故事及其验收场景

Cucumber 提供了一系列的 Annotation 来帮助我们验收场景文本与测试代码粘连在一起。

图表 4 实现验收测试 -1

图表 5 实现验收测试 -2

接下来,当运行测试时,你就可以得到一份漂亮的 html 报告

图表 6 测试运行入口

维护非端到端的验收测试

当团队开始编写验收测试之后,一般没过多久就会发现验收测试的开发进度越来越慢,而且有时遇到测试失败,但其实应用程序并没有缺陷的情况。验收测试对代码质量的要求也很高,相比单元测试,为了要达到测试所需的起始状态,验收测试的准备工作要更复杂。而且由于需要解析应用程序返回的数据,验收测试的断言也会更加琐碎。因此,团队最好尽早开始重构验收测试,下面的建议或许有用处:

  • 建立最小测试数据集并且尽可能隔离测试的数据。有时团队会发现两组测试由于依赖同一批数据而产生冲突,单独执行任一组测试都能通过,但一起执行就会失败。比如在示例代码中,对于不同的货件处理事件登记场景,验收测试都会注册一个新的货件。
  • 隐藏断言细节。这样可以减少重复代码,并提升测试的可读性。把琐碎的解析逻辑隐藏在领域语言编写的方法中。

例如:如果多个测试用例都会对货件的运输状态进行断言,可以把解析细节提取出来,这样可以去除重复代码,并且减少语法噪声。

图表 7 抽取断言

  • 尽可能使用已实现的功能来实现测试场景的准备。有一些步骤可能是多个测试用例都需要来准备数据的,可以把此类步骤抽取出来。这样也可以减少重复的代码,当应用程序随着需求变化时,验收测试会有更强的适应性,而且抽取出来的方法由于隐藏了技术细节,使用起来更简练。直接使用数据脚本的方案看起来很诱人,但一旦内部结构改变,数据脚本也得跟着改。

图表 8 抽取公共步骤

验收测试对实践部署流水线的团队有着重要意义,也是很大的挑战。希望大家都能找到合适自己的方法。最后介绍几个有用的测试库。

  • Moco ,当有外部系统集成需求时,集成测试和验收测试的一大利器。在示例代码中你可以找到一处例子。
  • GreenMail ,如果应用程序需要发送邮件的话,它可以提供一臂之力。不过在部署流水线上的端到端验收测试中,由于一般应用程序和测试并不运行在同一台机器上,很难对邮件进行直接的断言。这时一种方案是修改应用程序的架构,把发送邮件的实现分离到一个专用的应用中去并使用消息队列集成。那么在验收测试中,我们就可以通过监听对应的消息队列来断言了。
  • Awaitility ,在需要对异步处理进行断言时有所帮助。

作者简介

周宇刚是一位乐于磨练技艺的开发者。他的研究和兴趣包括 IT 架构、领域驱动设计和敏捷实践。


感谢崔康对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2013-12-30 10:323160

评论

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

云网资源如何搭乘「数字孪生」的快车道?

鲸品堂

数字孪生 设备 云网资源

apache,httpd服务启动报错解决方法【linux用日志排错方法

爱好编程进阶

Java 程序员 后端开发

如何使用Tomcat实现WebSocket即时通讯服务服务端

华为云开发者联盟

html5 spring tomcat 浏览器 websocket

8年开发架构师浅析SpringBoot的JVM的内存占用与Docker-spring

爱好编程进阶

Java 程序员 后端开发

keep-alive+导航守卫让缓存更精确

空城机

Vue 5月月更

[Day39]-[二叉树] 二叉搜索树中第K小的元素

方勇(gopher)

LeetCode 二叉树 数据结构算法

他教全世界程序员怎么写好代码,而且将所有答案写在这本书里!

博文视点Broadview

音视频行业玩家必读,如何实现生态合作+商业变现

华为云开发者联盟

音视频 华为云 实时音视频 实时音视频行业加速器 华为云SparkRTC

新作!分布式系统韧性架构压舱石OpenChaos

华为云开发者联盟

Serverless 容器 分布式系统 混沌工程 OpenChaos

网站开发进阶(五十三)浅谈JS、Ajax、JQuery之间的关系

No Silver Bullet

JavaScript jquery ajax 5月月更

kubernetes下的Nginx加Tomcat三部曲之二:细说开发

程序员欣宸

Java Kubernetes 5月月更

【PIMF】手把手教会在OpenHarmony仓库不使git命令提交PR参与社区贡献

离北况归

OpenHarmony Openharmony啃论文俱乐部 PIMF团队

网站开发进阶(五十)IE浏览器JS调试方法详解

No Silver Bullet

调试 5月月更

浪潮云x乡村振兴:在烟台 十字融合勾勒数字乡村全新蓝图

云计算

2021秋招必刷题:Redis+Mybatis

爱好编程进阶

Java 程序员 后端开发

C语言_函数封装、变量的作用域

DS小龙哥

5月月更

深入了解python字典的有序特性

红毛丹

python 3.5+ 5月月更

NFT 智能合约中的元数据(Metadata)

devpoint

区块链 智能合约 元数据 NFT

一种基于事件驱动思想的 SAP 系统集成二次开发方法介绍

汪子熙

云计算 SAP 二次开发 5月月更

知名整机厂商中科曙光加入,携手龙蜥社区共建应用生态

OpenAnolis小助手

龙蜥社区 CLA 龙腾计划 中科曙光

兼容PyTorch,25倍性能加速,OneFlow“超速”了

OneFlow

人工智能 深度学习 性能优化 oneflow

线程简介

急需上岸的小谢

5月月更

30天消化MyBatis源码解析笔记,吊打面试官,offer接到手软

爱好编程进阶

Java 程序员 后端开发

95% 的算法都是基于这 6 种算法思想

爱好编程进阶

Java 程序员 后端开发

01-Linux 系统简介

爱好编程进阶

程序员 后端开发

为什么企业一定要拥有知识管理的能力

小炮

企业知识管理

12-Redis持久化

爱好编程进阶

Java 程序员 后端开发

25 网站应用攻击与防御

爱好编程进阶

Java 程序员 后端开发

2021腾讯最新面经总结:面试题库+实战笔记

爱好编程进阶

Java 程序员 后端开发

60KX17薪的面试题是什么样的,需要具备什么技术?首发

爱好编程进阶

Java 程序员 后端开发

AI 顶会 NeurIPS 收录:淘系技术自研3D AI 算法

爱好编程进阶

Java 程序员 后端开发

用Spring实现非端到端验收测试_语言 & 开发_周宇刚_InfoQ精选文章