当前位置: 首页 > news >正文

“饿了么”​从测试标准化走向测试服务化

导读:公司的工程能力会直接影响公司的持久创新力以及在市场上的竞争力。只有不懈追求卓越的工程能力,才能拥有长期的核心竞争力,才能为每个用户、每个企业客户乃至整个社会创造价值。在这个背景下,研发效能的提升就成了很多产品技术部门的重要目标。

作者:邱化峰

来源:华章计算机(hzbookjsj)

 

目前,国内已有不少互联网公司成立了工程效能或工程效率部门,试图通过专项技术组织文化、流程等方面的优化来促进研发效能的整体提升。本文为你分享研发质量保障与工程效率的优秀案例:饿了么从测试标准化走向测试服务化。

 

案例背景

传统行业和互联网行业对产品可靠性的要求是不一样的,比如美国航天局的Gensym项目,其对可靠性的要求远远高于互联网产品。互联网产品的性质决定了测试的指标要求,测试指标的要求不同又导致了测试策略和测试方法的不同。互联网企业要求速度快、质量高,但由于互联网企业的链路比较长,在这种背景下测试如何开展和执行是值得每个企业关注的问题。

 

为此我们要解决的核心问题有:

1)沟通问题。测试工作的大部分成本都花在沟通上,在长链路的互联网企业中,上下游沟通费时费力,一个完整的测试流程往往需要多个部门联动。

2)效率问题。我们一般会评估开发测试比,一般的情况是3∶1,如何将这个比例提升到8∶1则是我们想要解决的问题。

3)指标的可视化问题。测试工作中我们常常无法衡量测试的覆盖度和开发的质量。

为解决上面的三个问题,我们制定的措施是标准化、流程化和可视化。

 

案例实施

1.接口的自动化测试概述

01

标准化

标准化的指标包括需求文档的标准化、Case的标准化、代码编写及注释的标准化、Bug提交的标准化、测试环境搭建的标准化、验收的标准化、发版的标准化。

1)需求文档的标准化。

有统一的需求说明书模板,需要按照标准模板来填写需求文档。

2)Case的标准化。

我们在工作中执行自己的测试用例不会有什么障碍,但执行别人写的用例时会不知所措,可能在多次阅读之后才理解该用例检查的是什么功能点,也可能在看到测试用例后不知道该如何执行。测试用例用来指导我们的测试,它的可读性、可操作性非常重要。因此,在测试用例编写风格及样式的统一问题上有如下标准:

  • 用例标题简明,清晰反映测试点。建议采用“功能点-测试点”的格式,方便执行者迅速理解用例对应哪个功能下的哪个测试点。

  • Precondition应标明前提条件,对于使用固定或特殊账号、数据才能执行的用例需标名测试数据,对于步骤较多或场景复杂的用例建议添加简单的目的描述或场景描述,便于执行者理解。

  • 测试步骤描述要简洁、清晰,一步就是一步,避免将多个步骤或操作放在一句话中。对于一个step下有多个小步骤的情况,应为step下的每个小步骤标明“1、2、3”,每个小步骤描述应尽量简洁,避免执行者花很长时间阅读、理解一个步骤所要表达的操作。

  • 每个step的期望结果描述应简洁、清晰,可将输出结果细化成几个不同的小点,避免将所有期望结果杂糅在一句话中。如一个步骤可能对应多个要检查的输出,建议将每个小的输出标明“1、2、3”,每个小输出的文字描述应尽量简洁、易懂。

  • 插入截图,方便读者理解。有时case中的文字越多反而可读性越差,阅读者或执行者很难明白用例所要表达的意思,但是截图可以代替很多文字描述,很直观地向读者展示所要进行的操作和所期望的结果,便于读者理解。每个case至少应有1张或2张截图,对于要切换页面的地方也可以放上截图。

  • 用例的划分应单一。一个测试用例只检查一个功能下的一个测试点,这样我们测试了哪些情况,以及哪些功能点我们在重点测试,一目了然。如果一个用例检查的点太多,会导致用例的目的不清晰。

  • 区分用例优先级。区分用例优先级,才能使执行测试的人在规定时间内优先确保主要功能及流程正常,如登录、注册等影响主流程的用例优先级应标为high,普通功能但非主流程用例应标为medium,字段长度校验、必填项校验等不影响上线质量的优先级应标为low。如果不明确区分优先级,当测试时间较短时,执行测试的人将浪费大部分时间在字段长度等次要问题的检查上,而忽略流程性的Bug,导致上线延迟。

  • 设计用例需考虑各种异常场景。

3)代码编写及注释的标准化。

开发时必须按照Google Java Style去编写代码,并且使用CheckStyle(http://checkstyle.sourceforge.net/reports/google-java-style-20170228.html)去检查你编写代码是否符合规范。如果提交的代码不符合标准会被自动拒绝,符合标准则会自动触发FindBugs(http://findbugs.sourceforge.net/index.html)。

4)Bug提交的标准化。


Bug提交的标准化包含如下几点。

  • Bug描述:描述要简单、清晰。

  • 重现步骤:需要提供完整的重现步骤。

  • 预期结果:期望值、期望结果或预期行文。

  • 实际结果:实际值、实际结果或实际行文。

  • 问题原因:需要提供堆栈或日志,并提供初步的问题原因。

  • 使用环境:发现的问题的环境。

  • 产品的版本:产品Release的版本号信息。

  • 外部数据编号:数据依赖及数据。

  • 视频及截图:视频或截图中会对每个操作步骤加文字和断点,并有详细的备注信息。

5)测试环境搭建的标准化。

测试环境的建立和部署应按照以下步骤来执行:

  • 创建裸机模板。

  • 从裸机模板创建实例机。

  • 通过自动化脚本安装服务端和客户端,并在实例机上启动相关的服务。

  • 验收完成后,自动回收实例机。

6)验收的标准化。

验收环节需要满足以下标准:

  • 自动检查每个Bug提交时是否符合Bug修复的代码提交规范。

  • Bug信息完整,需要包含BugID、注释、提交者ID。

  • 需要按照测试环境搭建的标准化来验收Bug是否修复。

  • Bug验收后,触发代码Review平台。

  • Review平台自动创建任务给Code Review工程师。

  • Code Review工程师由架构师及以上级别的人来担任。

  • Code Review工程师会判定本次修改的代码是否可以合并到Master。判定通过后合并到Master,并重新触发整个流程。

7)发版的标准化。

发版的标准化包含以下标准:

  • 自动检查版本是否可以自动升级到新的版本。

  • 自动检查和执行SQL。

  • 如果升级失败,是否能够回滚到之前的版本。

02

流程化

流程化包含以下两个方面。

  • 自动化流程周转工具整个研发过程采用了工作流的工具,会自动创建新的任务给下一个环节的人。

  • 持续不断地通过报表改善现有流程:整个研发流程和验收流程会以每三个月的频率根据统计出来的数据进行改进。流程不会一成不变,而是持续地改进。

03

可视化

可视化即可度量化,包括以下两个方面。

  • 开发质量的可度量化:需要关注修复Bug的一次通过率、开发新功能的一次验收通过率、开发的周及月产出。

  • 测试指标的可度量化:测试验收的Bug及新功能的数量、自动化脚本的维护量、覆盖率。

 

2.实施的步骤

01

测试数据的自动化准备

在实际工作中,创建测试数据往往需要很长的时间,最令人头疼的事情莫过于准备测试数据。那么,如何自动化地生成高质量的测试数据呢?

数据的生成来讲,生成数据有5种方法。

  • 手工生成:按照测试用例去设计数据。

  • 自动生成:完全随机或有目标地随机生成测试数据。

  • 数据库注入SQL:通过SQL来生成测试数据。

  • 第三方工具:有很多针对特定类型数据生成测试数据的工具,例如邮箱地址、IP、邮编地址等,这些数据本身有很强的固定性,所以可以使用开源工具来帮助生成测试数据。

  • 面向路径的数据生成:通过分析代码里面的常量、变量及逻辑约束来获取测试数据,这种方法也可以叫作有目标地生成数据。

数据生成的策略来讲,生成数据有4种方法。

  • 任意测试数据的生成:按照数据类型随机生成测试数据。

  • 有目标的数据生成策略:基于数据流程图(CFG)、数据依赖流(DDG)、程序依赖流(PDG)、系统依赖流(SDG)、扩展的有限状态机(EFSM)。

  • 数据变异(Mutation Testing)

  • 智能化的数据生成:如遗传算法来生成数据等。

我们最后采用的策略是:人工生成数据,并对数据进行分析和抽取,加入边界值及异常值,再通过正交试验去除无效数据,最后通过代码覆盖率和变异分数进行二次筛选。

代码覆盖率用来证明代码是否被执行过,变异分数用来衡量数据的质量。正交测试用例设计又称为组合试验法。利用场景法来设计测试用例时,作为输入条件的场景非常庞大,会给软件测试带来沉重的负担,而如果舍弃一些场景,测试的覆盖度可能不够,会漏测一些Bug。因此,为了有效地减少测试缺陷的遗漏,合理地减少测试的工时与费用,我们可利用正交实验设计方法,也就是我们所说的用最小的用例来覆盖更多的代码。

 

02

测试覆盖率和开发质量可视化

一开始我们使用原生Jacoco获取覆盖率,发现覆盖率非常的低,大约在20%~30%,于是我们又用Sonar做了一版基于类修改的代码覆盖率,将代码覆盖率提升到了40%~60%,但这仍然比较低。后来我们又用抽象语法树(Abstract Syntax Tree,AST)做了一版基于方法改动的代码覆盖率,将代码覆盖率提升到了70%以上,有效地体现出测试的执行过程。AST的原理如图1所示。

图-1 AST方法的原理

 

上图中,我们通过Git Diff拿到修改的代码行数,再通过AST获取每个方法的起始行数和终止行数,这样我们就可以很容易地判断出是哪个方法进行了修改。

当然,我们也可以通过Git Diff识别出哪个类进行了修改,然后针对这些修改的类,获取每个类下面的方法体的内容,逐个比较。这个过程类似于文本比较,只要比较方法体的内容是否被修改过即可。

无论使用哪种方法,我们的目的很明确,就是需要知道哪些方法被修改过了,这样测试人员就可以有针对性地开展测试工作。

我们将每次测试覆盖的基于方法层面的覆盖率收集到数据库中,就可以获取最常出现的10个类修改,通过Git的提交来分析测试和研发是否有问题。排名前10修改的类如图2、图3所示。

 

图2 排名前10修改的类一

图3 排名前10修改的类二

 

我们可以看到,修改最多的是数据存取对象DAO(Data Access Object,主要位于业务逻辑和持久化数据,实现对持久化数据的访问)。通过Git的日志我们可以看到,在2018年3月12日有3次代码的提交,通过这3次代码的提交我们可以推断出,数据库的设计并没有经过严格的Review,否则不会出现一天修改3次的情况。

数量仅次于DAO的是数据传输对象DTO(Data Transfer Object,数据传输往往是数据访问对象从数据库中检索的数据),它不具有除了存储和检索数据外的任何行为。

接下来的是视图对象VO(View Object),它用于展示层,作用是把某个指定页面(或组件)的所有数据封装起来。

修改最多的类LogisticsOrder体现出了业务的需求变化。这个表几乎每个月都在变化,而且多为一个月两次,这与我们的发版周期基本一致。但是,每次发版都需要调整表结构本身也是一个问题。数据库字段有增加也有删除,试想一下,如果你的业务量比较大(千万级别以上),对数据库进行增加和删除将非常危险。根据这个表的变化我们可以初步判定,产品并没有做好长期规划,架构设计也存在问题。这些问题其实就是所谓的“技术债”。既然是“债”,早晚是要还的,如何能够在快速迭代的过程中减少技术债,也是一个需要我们深思的问题。

排名前10的修改方法如图4所示。

 

图4 排名前10的修改方法

 

排名前10的修改方法主要集中在ShelfGroupStockInfoMapper类下,而这个类下最多的修改方法类型是查询类型,全部归属于读取操作。因此,我们有没有必要抽取出一个查询模板来减少更改的次数呢?

从命名上我们就可以看出,queryLogisticsShelfgroupCount比queryLogisticsShelfgroup多做了一个计数。如果queryLogisticsShelfgroup是必须调用的,那么queryLogistics-ShelfgroupCount是否还有必要存在,是否可以在queryLogisticsShelfgroup里进行处理?

同时,我们也可以从图中看出开发人员是否在做单元测试。很显然,开发人员并没有做任何单元测试,他们写的测试是用来运行主流程的。

通过观察排名前10的修改类和修改方法,我们验证了80%的Bug来自20%的代码这个说法。那么,是不是我们做好这20%的代码的测试工作就能够降低Bug出现的概率呢?

针对这个问题,我们需要对测试人员的技能进行分析及改善,如图5所示。

 

图5 测试人员技能的分析与改善

通过对测试覆盖率的总结,我们发现:

1)覆盖率是有局限性的,不要盲目追求高覆盖率。

2)测试覆盖执行是一个有限的、渐进的过程。

3)测试覆盖是一个自我认知的过程,也是团队自我认知的过程。

4)漏测的场景和发现的Bug有很高的相似度。

随着Case的不断累积,我们的Case经历了三个过程。

1)无自动化的Case阶段:这个阶段比较痛苦,出现Bug的时候只能全部靠人工回归,经常会出现旧的Bug未解决,新的Bug又出现了的情况。

2)Case自动化建设阶段:部门内建设了自动化的接口测试平台,帮助功能测试人员生成接口的测试用例,先后经历了从HAR生成接口测试用例,从日志来生成测试用例,通过Json Schema自动生成Case的三个阶段,同时我们要求测试人员每天补录10个接口的Case,Case的数量就膨胀起来了。这个时候回归的Case单个APPID会有上万个Case,回归的时间也越来越长了,即使Case回归完成后有邮件来告知测试人员哪些接口失败了,但是大量失败的接口的维护成本很高,测试人员还是不能够做到上线之前把所有的失败Case都维护一遍。

3)Case的优化阶段:第一个优化Case的方法是将测试数据和测试用例分离,但这仍然满足不了我们的需求。由于每次出现新的Bug都是跟代码有关联的,于是我们改变了测试Case的生成和维护方式,将Case和代码提交的Commit ID关联。当所有新的Case都和Commit ID进行关联后,所有的Case都是基于这次代码变动。接下来,可以从以下三个角度进行优化。

  • 基于历史失败的Case:优先执行那些之前失败的Case。失败的Case发现Bug的概率更高,因此可以优先执行。

  • 基于方法的复杂度:复杂度较高的方法的修改更容易引发Bug。复杂度较高的方法一般是难以理解和维护的,因此回归的时候需要优先考虑和这些复杂度有调用关系的Case。与此同时,复杂度也可以作为提供给开发进行代码优化的一个指标。

  • 基于相似性:不要把所有的鸡蛋都放在一个篮子里,也要从风险的角度考虑。

 

案例总结

1)测试服务化需要以自动化和标准化为前提。

2)需要可视化的指标来度量和提高测试服务。

3)通过测试服务化来保障高质量的服务。

 

 

作者介绍

邱化峰,测试开发专家,拥有10年IT从业经验。就职于饿了么,主要负责产品线的开发和质量保证工作,带领团队开发接口的测试框架、精准的代码覆盖率框架。曾先后做过HIS系统和游戏开发,参与了Gensym、Versata旗下的多款产品的持续改善和持续集成,对于持续集成、持续交付及软件产品线有深刻的理解。

 

以上摘自《研发质量保障与工程效率》。

 

如想了解更多知名企业的典型实践案例和优秀实践经验,请阅读《研发质量保障与工程效率》一书。

这是一部从实践角度探讨企业如何保障研发质量和提升工程效率的著作,它将帮助企业打造一个强战斗力、高效率的研发团队。

本书汇聚了阿里巴巴、腾讯、字节跳动、美团、小米、百度、京东、网易、科大讯飞等30余家中国一线互联网企业和领先科技企业在研发质量保障和工程效率方面的典型实践案例和实践经验。从基础设施到技术架构、从开发到测试、从交付到运维、从工具框架到流程优化、从组织能力到文化塑造,几乎涵盖了研发质量和工程效率的方方面面。

本书“轻理论,重实践”,全部以案例形式展开,每个案例都包含案例综述、案例背景、案例实施和案例总结4个模块。读者可以跟着作者的思路,找到各种问题的解决方案。

 

点击链接了解详情并购买

重磅

更多精彩回顾

书讯 |10月书讯(下)| 双节同庆,读书正当时

书讯 |10月书讯(上)| 双节同庆,读书正当时

资讯 |DB-Engines 10月数据库排名:“三大王”无人能敌,PostgreSQL紧随其后

上新 | 百度官方出品 | 全面解读PaddlePaddle,零基础快速入门深度学习
书单 | 开学季——计算机专业学生必读的10本畅销经典

干货 | 数据分析必读干货:简单而实用的3大分析方法

收藏 | (万字长文)Spring的核心知识尽揽其中

视频 | 大佬出镜推荐不可不读系列——程序员陈彼得

赠书 | 【第26期】Coroutines(协程)我是这样理解的!

点击阅读全文购买

相关文章:

  • 机器学习领域已经百花齐放了,为何还要搞个TinyML?
  • 京东程序员狂欢节,上百本好书一抢而空
  • 《数字孪生体技术白皮书》重磅发布(附完整版下载)
  • 1024长沙程序员节北京移动开发者大会,独家现场报道
  • 为什么数字中台是企业应用新基建?
  • 作者专访 | 从受益者到贡献者,朱凯的 ClickHouse 升级之路
  • 这本书堪称仿真领域的“隆中对”
  • 当Nginx遇到重定向
  • “深入浅出”《Head First Kotlin程序设计》
  • RPA的总成本和总收益分析
  • 前端音视频之WebRTC初探
  • 如何成为一名优秀的策略产品经理
  • 【超干】用户画像产品化涵盖的功能模块以及应用场景
  • WebAssembly前世今生
  • 【独家现场】2020国际金融科技论坛
  • CSS魔法堂:Absolute Positioning就这个样
  • ES6系列(二)变量的解构赋值
  • Fabric架构演变之路
  • open-falcon 开发笔记(一):从零开始搭建虚拟服务器和监测环境
  • supervisor 永不挂掉的进程 安装以及使用
  • unity如何实现一个固定宽度的orthagraphic相机
  • win10下安装mysql5.7
  • Zsh 开发指南(第十四篇 文件读写)
  • 机器学习 vs. 深度学习
  • 简单实现一个textarea自适应高度
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 前端攻城师
  • 悄悄地说一个bug
  • 学习HTTP相关知识笔记
  • 一、python与pycharm的安装
  • 用简单代码看卷积组块发展
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • Prometheus VS InfluxDB
  • 正则表达式-基础知识Review
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • #define用法
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • ()、[]、{}、(())、[[]]命令替换
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (二)JAVA使用POI操作excel
  • (二)pulsar安装在独立的docker中,python测试
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (原)Matlab的svmtrain和svmclassify
  • (转)Scala的“=”符号简介
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • (转载)PyTorch代码规范最佳实践和样式指南
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .NET Micro Framework 4.2 beta 源码探析
  • .NET设计模式(7):创建型模式专题总结(Creational Pattern)
  • /run/containerd/containerd.sock connect: connection refused
  • :中兴通讯为何成功