写点什么

一个遗留系统自动化测试的七年之痒

  • 2017-01-08
  • 本文字数:3124 字

    阅读完需:约 10 分钟

项目从 2009 年开始启动,采用的是 TDD 的开发方式。在这之后的过程中,团队做过各种尝试去调整自动化测试的策略去更好的适应不同阶段项目的特征,比如调整不同类型测试的比例,引入新的测试类型等。

七年之痒 - 痛点

随着项目走到了第七个年头,一系列的变化在不断发生,比如技术上引入了微服务、EventStore 等,业务变得越来越复杂,子系统变得更多,更多的人员加入,开始实施按月发布等,这些因素交织在一起凸显出自动化测试的滞后。首先是从团队成员感知到的一些痛点开始的:

  1. 质量下降 - 这个体现在部署到测试环境的代码质量较差,常常就是新版本部署上去之后某个核心功能被破坏,要么是新功能破坏了老功能,要么是 bug 的修复把其他功能破坏。
  2. 测试不稳定 - QA 有很长的时间在等待修复或新功能提交出包,而这个等待可能是几个小时也有可能是几天。除去网络问题、部署流水线的复杂性等因素,自动化测试的不稳定性也导致出包的速度也受到了影响。大家往往更关注于怎么能把测试通过了能够出一个包,而却忽略了我们该怎么去处理一个不稳定的测试。下图的 run2, run3 正是大家在不断的尝试去 rerun 挂掉的测试。
  3. 团队越来越忙,开始陷入恶性循环 - 随着功能的逐渐增多,每个月上线的回归测试列表越来越长,QA 需要花更多的时间去做重复的回归测试,新功能的测试和回归测试的压力都很大,甚至有的时候根本都没有时间去 review 下一个阶段的需求,更别提其他一些更有价值的事情。往往回归测试做不完就不得不往下一个阶段推,这种不断往复导致大家对发布的产品信心严重不足。

在这种情况下,自动化测试的有效性和完备性都受到了质疑。本来期望自动化测试能够帮助我们构建一张安全的防护网,保证主干业务不被破坏;随着 pipeline 频繁的去执行,及时反馈问题,不要等到测试环境才暴露出来;同时能够把 QA 重复的手工测试时间释放出来,去做一些更有价值的事。可是根据团队所感受到的痛点,我们觉得自动化测试已经不仅没有帮助我们,反而却在一定程度上给团队带来了干扰。

问题分析

自动化测试到底出了什么问题?我们从现有 UI 测试入手开始分析,发现了以下典型现象:

最有价值的场景没有被覆盖

虽然有较多的测试场景,但是体现出核心业务价值的场景却稀少。我们都知道 80/20 原则,用户 80% 的时间在使用系统中 20% 的功能,如果大部分的 UI 测试是在测另外那 80% 的功能,这样一个覆盖给团队带来的安全指数是很低的。

失效的场景

功能已经发生了变化,可是对应的 UI 测试并没有变,至于它为什么没有挂掉,可能有一些侥幸的因素。比如现在点了确认按钮之后新增了弹窗,而测试并没有关掉弹窗,而是通过 URL 跳转到了别的页面,也没有验证弹窗的新功能是否工作,既有的实现方式确实会使得测试一直通过,但是没有真的验证到正确的点。

重复的测试

同样的测试在 UI 层跟 API 层的重合度较高,有的甚至是 100%。比如搜索用户的功能,分别去按照姓、名、姓的一部分、名的一部分、姓➕名等等各种组合去验证。我们不太清楚在当时是基于怎样的考虑留下了这么多跟 UT/API 测试重复的用例,但是在现阶段分析之后我们觉得这是一种没必要的浪费。
另外,不同的测试数据准备都是 UI 测试执行出来的,很多场景都用到了相同的步骤,我们觉得这也是一种重复,可以通过其他方式来实现。

解决问题

这些问题从某种角度上都暴露出了 UI 测试年久失修,没有得到好的维护,而新功能的自动化测试又在不断重蹈覆辙,问题积攒到一起暴发出来使得大家开始重视自动化测试。
有痛点并且找到问题了,下一步就是解决问题。我们分了两步来走,第一个就是对已有 UI 测试的优化,第二个是对新功能的自动化测试策略的调整。

已有 UI 测试的优化

针对已有的自动化测试,再回过头逐个去审查 UT/API/UI 测试代价太高,我们只能是从 UI 测试优化入手。针对前面提到的 3 个问题我们逐一去攻克:

  1. 识别系统关键业务场景 我们首先挖掘出系统中用户要达到的各个业务目标,根据不同的业务目标梳理出不同的业务场景,然后将这些发给客户去评审。客户对我们总结出的这些场景很认可,只是把每个业务目标都赋予了一个优先级,为后续的编码实现给予了参考。
  2. 重新设计测试场景 UI 测试不是着重去测试某个功能是否工作,而更关注的是用户在使用系统时能否顺利实现某个业务目标,因此我们需要知道用户是怎么使用系统的。同样的目标,可能会有多个途径来完成,通过跟客户的访谈以及观察产品环境下页面的访问频度,我们重新规划了测试场景,期待能更贴近用户的真实行为,及时防御可能会导致用户不能顺利完成业务目标的问题。
  3. 优化测试数据准备,删除重复的测试 对于 UI 层的过度测试,直接删除和 API/UT 层重复的测试,保留一条主干路径用来测试系统连通性。

对于不同的业务场景可能需要准备的数据,我们舍弃了之前通过 UI 执行测试这种成本高的方式,转而以发 API 请求的方式来准备,这样降低了测试执行的时间,也使得测试更加的稳定。

重新梳理的测试场景帮我们构建了一张较为全面的安全防护网,覆盖了绝大部分的用户使用场景,大家的信心显著提高。如果核心业务受到破坏,立马就可以通过 UI 测试反馈给相关的人。执行更加稳定的测试也减少了大家对 UI 测试是否能真的发现问题的质疑,因为随机挂的频率大大的降低,一旦挂可能就是真的有 bug 了。

新功能自动化测试策略的调整

我们一般会以测试金字塔作为自动化测试的指导策略,下图是我们项目的测试金字塔。

为了避免新功能步旧功能的后尘,我们对自动化测试策略进行了调整。除去以不同层的数量分布来判定策略是否合理外,我们也更看重在这个数量下关键业务场景是否能被有效地覆盖,主要通过下面两种方式来保证:

  1. QA 及早介入自动化测试 质量是需要内建的,不是测出来的。QA 从一开始就介入整个流程之中,在 story 启动的时候会和 DEV 一起准备任务拆分。在后期验收 story 的同时也会验收单元测试,确保能在 UT/API/Contract 层实现的测试都在这些层面覆盖,不仅保证了底层测试的数量要够多,也确保了这么多测试覆盖的点都是合理有效的。在这个过程中,QA 把更多的测试思路传递给团队成员,引发大家更多的从质量角度去思考。
  2. QA 与 DEV 结对写 UI 测试 最后在整个功能做完的时候,QA 也会和 DEV 结对实现 UI 测试,涉及到现有测试场景的维护与更新。基于前面对底层测试的 review,大家对于整个功能的测试覆盖都有了一定程度的了解,对于 UI 测试要测得点也会较快的达成一致。另外,QA 在与 DEV 结对实现 UI 测试的时候,编码能力也得到了提高。

在推动 QA 更多参与底层测试的过程中,我们更多的从测试角度去影响团队,增加了团队的质量意识。QA 的时间被释放出来了,去做了更多有价值的事,比如探索性测试,Log 监控与分析,安全测试,产品环境下用户行为分析等。这一些列活动的影响就是产品的质量顺便得到了提升。

总结

等我们把已有功能 UI 测试优化完,新功能的自动化测试策略开始落实到全组,已经是半年以后的事了。我们慢慢的感受到一切都在回归正轨,之前的痛点在逐步消去,团队交付的节奏也越来越顺畅,对发布产品的信心也更强了。

回顾这个遗留系统的自动化测试优化过程,我们有一些收获:

  1. 大家说到 UI 测试往往更倾向于如何编码实现,但我们希望开始 UI 测试的时候能多关注下测试用例的设计是否合理,是不是能够体现出业务价值
  2. UI 测试的用例和代码都是测试资产,需要跟产品代码等同对待,不能写出来就不管不顾,没有维护是不可取的
  3. 自动化测试不仅仅是 UI 测试,需要和 UT/API 等其他底层测试一起分工合作,作为测试策略的一部分来为产品质量保驾护航

感谢张凯峰对本文的策划, 木环对本文的审校。

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

2017-01-08 16:132760

评论

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

神经网络的激活函数为什么要使用非线性函数

王坤祥

神经网络 激活函数

经济大萧条对我的启示

Neco.W

创业 自我管理 职场 自我提升

DDD 实践手册(2. 实现分层架构)

Joshua

设计模式 领域驱动设计 DDD 系统架构 分层架构

韦小宝真的幸福吗 | Random Forest

张利东

Python 学习

InfoQ写作平台首秀,来个自我介绍

nuhcoad

个人感想

阿里巴巴Java开发手册泰山版解读

Bruce Duan

即将步入职场,忐忑而又期待的新人菜鸟

菜农阿飞

成长 新人

浅析 Cocoapods-Packager 实现

Edmond

ruby ios CocoaPods binary packager

怎样算是一个好的开发者?

水滴

开发者

MySQL中order by语句的实现原理以及优化手段

天堂

Java MySQL 性能优化

使用 jsDelivr 免费加速 GitHub Pages 博客的静态资源

mzlogin

CDN Jekyll GitHub Pages 个人博客

程序员陪娃漫画系列——魔方

孙苏勇

程序员 生活 陪伴 漫画

Day 47|Week 07-5 曾国藩家书|问学篇-学问何处何时都可做

熊小北同学

南丁格尔科普

小匚

高仿瑞幸小程序 00 准备工作

曾伟@喵先森

小程序 微信小程序 大前端 瑞幸

如何优雅滴在手机上跑Python代码

王坤祥

Python 移动应用 手机编程

Netty 源码解析(四): Netty 的 ChannelPipeline

猿灯塔

Java并发编程系列——锁

孙苏勇

Java Java并发 并发编程 多线程

消息队列Kafka - acks参数

Java收录阁

kafka

关于5G RCS的产品猜想

机器鸟

多云的一点思考

HU

把成功过成自己的生活

子铭

成功学 生活状态

死磕Java并发编程(7):读写锁 ReentrantReadWriteLock 源码解析

Seven七哥

Java并发 读写锁 ReentrantReadWriteLock

游戏夜读 | 2020周记(3.27-4.3)

game1night

HashMap 的 7 种遍历方式与性能分析

Bruce Duan

Java 性能 hashmap 遍历

说出来就不灵啦

伯薇

糊涂 活在当下 享受状态 生活状态 观察者

学习来应对创业的未知

Neco.W

创业 重新理解创业

从数据闭环谈微服务拆分

松花皮蛋me

微服务

为什么正在使用的Java版本跟环境变量的版本不一致

阡陌r

Java 踩坑

MySQL的死锁系列- 锁的类型以及加锁原理

程序员历小冰

MySQL

KubeFATE: 用云原生技术赋能联邦学习(一)

亨利笔记

人工智能 学习 FATE KUBEFATE

一个遗留系统自动化测试的七年之痒_软件工程_胡志芳_InfoQ精选文章