写点什么

在持续交付中加入自动化验收测试支持

2017 年 5 月 04 日

自动化验收测试是持续交付测试策略十分关键的一环,它为开发者们洞察系统行为提供了一个重要而不同的视角。Dave Farley 提出,保持验收测试持续运行和通过率是开发者们的责任,而不能依靠另外的 QA 团队去维护验收测试,导致拖累开发团队的进度。

独立软件开发者和顾问 Dave Farley Craft 2017 大会上讨论了持续交付中的验收测试。InfoQ 通过问答、总结和文章的形式对大会进行了报道。

Farley 说,好的验收测试可以看做系统行为的“可执行规范”。为了提高可读性,他建议开发者使用领域相关的语言编写测试,这样我们就更容易地向产品负责人、客户或是其他人解释测试的意义。另外,它还能让测试用例变得容易维护。

验收测试中的时间依赖性问题有两种解决方案。开发者可以选择忽略掉测试用例中的时间因素,但这就会导致某些情况无法被测试到,错过某些错误。另外的选择是把时间因素作为外部依赖。Farley 给出了一个如何控制时间因素的例子。例子中,他构造了一个可以提供时钟功能的桩(stub),为测试框架增加了操作时间因素的相关函数。

InfoQ 向 Dave Farley 提出了如下问题:是什么让验收测试变得如此之难,我们又如何做好验收测试?

InfoQ: 开发者应在什么情况下使用验收测试?验收测试的目的是什么?

Dave Farley: 在我看来,自动化验收测试是持续交付测试策略十分关键的一环。结合低层次的单元测试 — 最好是作为测试驱动开发(TDD)的成果 — 自动化验收测试可以向开发者们提供一个重要而不同的视角来观察系统运行行为。

验收测试可以从一个外部用户的角度,在近似于生产环境的测试环境中对系统进行评价。测试用例最好写成系统行为“可执行规范”的形式。

最后,在提供系统可执行规范视图、为用户提供功能性合约准则之外,验收测试还向开发者提供了自动化的功能完成定义(definition of done),还是演习自动化部署和系统配置的最佳机会。

InfoQ: 是什么让验收测试变得这么困难?

Farley: 耦合度!大多数组织没有努力将高层次功能测试用例与需要运行测试的系统进行解耦。因此,每当承担测试的系统发生变更时,测试用例就会失效,需要跟进修改。我的演讲主要就是围绕着这个问题展开的。为了达到目标,开发者可以引入几个技术,如领域特定语言(Domain Specific Language),它允许开发者把验收测试定义为“可执行规范”,而不仅仅是“测试”;测试隔离,让开发者撰写测试时,专注于系统的行为是“什么”,而不去考虑系统“如何”实现。这些技巧可以极大地帮助开发者,高效地维持测试用例(也就是系统规范)与需要被测试的系统之间的关注点分离。

在解决验收测试问题的实践中,我还学习到解决问题的关键还在于把责任交给正确的人。业界普遍存在的一个反模式,就是让独立的 QA 团队拖累开发团队,即使他们努力地实现测试的自动化。

如果开发者们能成功定义系统行为真实可用的“可执行规范”,那么当代码改动导致了测试用例失效,解决问题责任就落在了开发者自己身上。毕竟,一旦系统的改动导致测试失效,那是因为系统不能再满足功能规范(验收测试套件所描述的那些东西)。任何人都可以写测试,但开发者自己应当在测试首次可执行之后,就承担起保证测试可以正常运行的责任。

InfoQ:如何隔离不同的测试用例?

Farley: 我认为测试用例隔离有三层重要的层次:系统级别隔离,功能性隔离以及临时性隔离。

系统性隔离意味着开发者需要明确定义被测试系统间的边界。开发者需要精确地控制被测试系统的状态。为了达到这个目标,开发者应为被测试系统的边界撰写测试用例。在测试目标系统时,开发者不希望与上游系统进行交互,因为这意味着开发者将会对测试边缘情况失去足够的控制。开发者也不希望收集下游外部系统的结果。开发者希望做到的是,直接通过系统提供的任意形式的普通接口调用系统的行为,用桩(stub)替代外部依赖,以便收集系统的运行结果、设定正确运行时所期望的数据结果,或为引发系统行为注入相应数据。

通过功能性隔离,开发者可以使用存在于多用户系统中的天然边界,隔离不同的测试用例。开发者可以利用功能性隔离共享大型复杂系统的启动开销,同时使多个的测试用例在互不影响的情况下并行执行。原理十分简单,在测试用例的设置阶段,我们会创建新账号、定义产品和市场(或是可以代表系统内天然功能性边界的任何概念),并在单独的测试用例的上下文中使用。举个例子,如果是我为亚马逊网上书店编写测试用例,那么我会在每个测试用例开始前注册一个新账号,并创建一本可供售卖的书。

临时性隔离允许开发者在重复运行同一个测试的时候,都能得到相同的结果。再次说明,我不想在每个测试用例的最后清除系统资源,这样做的开销太大。我希望在同一个已部署系统中运行同一个测试用例。为了达到这个目标,我们使用刚才描述的功能性隔离方法,再结合使用命名代理(proxy-naming)技巧。当测试用例请求创建一个新账号或是一本书时,测试框架介入其中,为将要创建的新项目构造一个别名,而不是使用测试用例中的名字。在测试的作用域内,测试用例还是可以使用测试用例中的名字,但框架内部会将其映射为唯一的别名供测试使用。利用临时性隔离,开发者在重复运行同一个测试的同时,还能享受到功能性隔离带来的好处。

InfoQ:你建议开发者不应该使用“UI 录制回放”系统。可以解释下原因吗?它的替代品是什么?

Farley: 我希望可执行规范应专注于用户行为,而不是专注于用户界面的实现细节上。

如果测试用例被编码成诸如“placeOrder”(下订单)或是“payForPurchases” (支付交易)等等问题领域内的术语,我就可以为任意的系统界面使用同样的测试用例。假设系统提供了图形用户界面和 REST API 两种方式下订单,就能为两种通讯通道创建同一个测试规范,从而达到实现关注点分离的目标。如果能做到这一点,开发者可以更容易地应对相对较少的系统改动,比如重写 Web UI,使得已有的测试用例(可执行规范)仍能得到重复使用。

本质上,在 UI 录制回放系统中,测试的关注点是 UI,而不是用户所期望的系统行为。这也就意味着 UI 录制回放系统的测试关注点在于技术本身,而不在于系统行为。这就导致这样的测试用例总是很脆弱的,并且更容易由于目标测试系统中相对较小的改动而失效。因为从长远来看,它会带来很多额外的工作,因此我会避免使用这种测试。

InfoQ:如何让测试用例和验收测试变为更有效率的整体?

Farley: 效率在测试的每个环节都需要普遍保持。开发者应思考如何在正确的地方测试正确的内容。

我不希望在验收测试中为每行代码编写测试用例,这样的代价太大了。我会在 TDD 中编写测试用例,并期望更早地获得反馈结果。

我不希望为测试使用生产环境的数据,因为这样的数据太过庞大,还会降低测试环境的启动速度和测试用例的执行速度。我希望使用最小测试数据集,这样我才能更精确地定位到需要测试的行为。

我会针对应用中不常见的操作进行优化,比如账号注册等。这可以提高测试和应用的启动速度,我因此可以更快地得到验收测试的结果。

我会尽量避免在测试用例中使用睡眠或是等待命令。开发者经常会把这些命令当做狗皮膏药般隐藏竞态条件(race-condition)问题。它们会让测试用例变得低效,而且经常也没真正地解决竞态条件问题,只是把问题转移到了别的地方。

验收测试是高品质测试策略的重要部分,但不是唯一内容。它不应当替代 TDD,或是替代 TDD 中定义的底层单元测试。验收测试应当作为 TDD 重要的补充。

对于最简单的项目,我依然会为其使用最小化的开发流水线。这样的流水线包含如下内容:“提交测试”(TDD),“验收测试”(可执行规范)和“自动化部署到生产环境”。

查看英文原文: Automated Acceptance Testing Supports Continuous Delivery


感谢薛命灯对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2017 年 5 月 04 日 19:001576

评论

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

MySQL的锁

一个大红包

3月日更

一个合格的CloudNative应用:程序当开源软件编写,应用配置外置

华为云开发者社区

云原生 华为云 Cloud Native CCE CSE

Java面试必看!阿里(嵩山版)分布式核心原理笔记来了

Java架构追梦

Java 阿里巴巴 架构 面试 架构分布式

区块链数字资产追踪系统解决方案,打造资产流通追溯体系!

源中瑞-龙先生

解决方案 #区块链# 资产追踪

干货 | 万字详解整个数据仓库设计体系

五分钟学大数据

大数据 数据仓库 28天写作 3月日更

论文免费开源:NB-IoT智慧路灯监控系统

不脱发的程序猿

论文 28天挑战 3月日更 NB-IoT智慧路灯 大学生毕业

源中瑞智慧社区解决方案,社区服务平台

13530558032

智慧党建信息管理平台系统建设

13530558032

阿里巴巴最新推出王者笔记:“Spring MVC源码与实践”

周老师

Java 编程 程序员 架构 面试

uni-app跨端开发H5、小程序、IOS、Android(三):理解uni-app框架MVVM思想

黑马腾云

微信小程序 uni-app android iOS Developer 3月日更

北京市工贸技师学院加入ACA世界大赛,坚持以赛促教,以赛促学

Adobe国际认证

「面试高频」秒杀架构的设计套路,你值得拥有

我爱娃哈哈😍

架构设计 架构设计实战 秒杀架构

Python if __name__ == ‘main’ 的作用介绍

HoneyMoose

在线数据迁移,数字化时代的必修课 —— 京东云数据迁移实践

京东科技开发者

数据库 数据迁移

LeetCode题解:213. 打家劫舍 II,动态规划(不缓存偷盗状态),JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

安卓嵌入式底层开发!整理出这份8万字Android性能优化实战解析,已开源

欢喜学安卓

android 程序员 面试 移动开发

如何批量下载YouTube视频到本地

科技猫

软件 音视频 经验分享 资源分享 工具分享

EGG Network公链技术创新,EFTalk打造高效全能公链

币圈那点事

区块链

使用“零信任”,不惧“内部威胁”!

龙归科技

管理 数据完整性 零信任 内部威胁

Python 生成 QR 二维码

HoneyMoose

一文搞懂三级管和场效应管驱动电路设计及使用

不脱发的程序猿

电路设计 三极管 28天挑战 3月日更 场效应管

GO训练营第10周——日志&指标&链路追踪

Glowry

收藏!Linux常用命令合集

roseduan

Linux

区块链电子发票平台,区块链电子发票优势

13530558032

快点来学吧!Android性能优化面试题集锦,深度解析,值得收藏

欢喜学安卓

android 程序员 面试 移动开发

冲击大厂!阿里P9纯手打Java面试小抄(21版)在GitHub上已获80万star

云流

Java 程序员 面试

Python 打印回车换行

HoneyMoose

【LeetCode】设计停车系统Java题解

HQ数字卡

算法 LeetCode 28天写作 3月日更

跟我学ModelArts丨探索ModelArts平台个性化联邦学习API

华为云开发者社区

联邦学习 API 华为云 modelarts AI算法

Java 和 Python 关于 % 的那些坑

与你一起学算法

Java Python

2B营销路径: 9大步骤自我拆解

boshi

营销数字化 七日更

演讲经验交流会|ArchSummit 上海站

演讲经验交流会|ArchSummit 上海站

在持续交付中加入自动化验收测试支持-InfoQ