项目从2009年开始启动,采用的是TDD的开发方式。在这之后的过程中,团队做过各种尝试去调整自动化测试的策略去更好的适应不同阶段项目的特征,比如调整不同类型测试的比例,引入新的测试类型等。
七年之痒 - 痛点
随着项目走到了第七个年头,一系列的变化在不断发生,比如技术上引入了微服务、EventStore等,业务变得越来越复杂,子系统变得更多,更多的人员加入,开始实施按月发布等,这些因素交织在一起凸显出自动化测试的滞后。首先是从团队成员感知到的一些痛点开始的:
- 质量下降 - 这个体现在部署到测试环境的代码质量较差,常常就是新版本部署上去之后某个核心功能被破坏,要么是新功能破坏了老功能,要么是bug的修复把其他功能破坏。
- 测试不稳定 - QA有很长的时间在等待修复或新功能提交出包,而这个等待可能是几个小时也有可能是几天。除去网络问题、部署流水线的复杂性等因素,自动化测试的不稳定性也导致出包的速度也受到了影响。大家往往更关注于怎么能把测试通过了能够出一个包,而却忽略了我们该怎么去处理一个不稳定的测试。下图的run2, run3正是大家在不断的尝试去rerun挂掉的测试。
- 团队越来越忙,开始陷入恶性循环 - 随着功能的逐渐增多,每个月上线的回归测试列表越来越长,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个问题我们逐一去攻克:
- 识别系统关键业务场景
我们首先挖掘出系统中用户要达到的各个业务目标,根据不同的业务目标梳理出不同的业务场景,然后将这些发给客户去评审。客户对我们总结出的这些场景很认可,只是把每个业务目标都赋予了一个优先级,为后续的编码实现给予了参考。
- 重新设计测试场景
UI测试不是着重去测试某个功能是否工作,而更关注的是用户在使用系统时能否顺利实现某个业务目标,因此我们需要知道用户是怎么使用系统的。同样的目标,可能会有多个途径来完成,通过跟客户的访谈以及观察产品环境下页面的访问频度,我们重新规划了测试场景,期待能更贴近用户的真实行为,及时防御可能会导致用户不能顺利完成业务目标的问题。
- 优化测试数据准备,删除重复的测试
对于UI层的过度测试,直接删除和API/UT层重复的测试,保留一条主干路径用来测试系统连通性。
对于不同的业务场景可能需要准备的数据,我们舍弃了之前通过UI执行测试这种成本高的方式,转而以发API请求的方式来准备,这样降低了测试执行的时间,也使得测试更加的稳定。
重新梳理的测试场景帮我们构建了一张较为全面的安全防护网,覆盖了绝大部分的用户使用场景,大家的信心显著提高。如果核心业务受到破坏,立马就可以通过UI测试反馈给相关的人。执行更加稳定的测试也减少了大家对UI测试是否能真的发现问题的质疑,因为随机挂的频率大大的降低,一旦挂可能就是真的有bug了。
新功能自动化测试策略的调整
我们一般会以测试金字塔作为自动化测试的指导策略,下图是我们项目的测试金字塔。
为了避免新功能步旧功能的后尘,我们对自动化测试策略进行了调整。除去以不同层的数量分布来判定策略是否合理外,我们也更看重在这个数量下关键业务场景是否能被有效地覆盖,主要通过下面两种方式来保证:
- QA及早介入自动化测试
质量是需要内建的,不是测出来的。QA从一开始就介入整个流程之中,在story启动的时候会和DEV一起准备任务拆分。在后期验收story的同时也会验收单元测试,确保能在UT/API/Contract层实现的测试都在这些层面覆盖,不仅保证了底层测试的数量要够多,也确保了这么多测试覆盖的点都是合理有效的。在这个过程中,QA把更多的测试思路传递给团队成员,引发大家更多的从质量角度去思考。
- QA与DEV结对写UI测试
最后在整个功能做完的时候,QA也会和DEV结对实现UI测试,涉及到现有测试场景的维护与更新。基于前面对底层测试的review,大家对于整个功能的测试覆盖都有了一定程度的了解,对于UI测试要测得点也会较快的达成一致。另外,QA在与DEV结对实现UI测试的时候,编码能力也得到了提高。
在推动QA更多参与底层测试的过程中,我们更多的从测试角度去影响团队,增加了团队的质量意识。QA的时间被释放出来了,去做了更多有价值的事,比如探索性测试,Log监控与分析,安全测试,产品环境下用户行为分析等。这一些列活动的影响就是产品的质量顺便得到了提升。
总结
等我们把已有功能UI测试优化完,新功能的自动化测试策略开始落实到全组,已经是半年以后的事了。我们慢慢的感受到一切都在回归正轨,之前的痛点在逐步消去,团队交付的节奏也越来越顺畅,对发布产品的信心也更强了。
回顾这个遗留系统的自动化测试优化过程,我们有一些收获:
- 大家说到UI测试往往更倾向于如何编码实现,但我们希望开始UI测试的时候能多关注下测试用例的设计是否合理,是不是能够体现出业务价值
- UI测试的用例和代码都是测试资产,需要跟产品代码等同对待,不能写出来就不管不顾,没有维护是不可取的
- 自动化测试不仅仅是UI测试,需要和UT/API等其他底层测试一起分工合作,作为测试策略的一部分来为产品质量保驾护航