360Qtest团队 自动生成 case 在 SDK 项目的 mock 测试中的应用 (理论篇)

simple · 2016年10月19日 · 最后由 退之 回复于 2022年08月18日 · 5201 次阅读

读完整篇文章可能需要 15 分钟,文章太长,分了两部分发
关键字:移动端 SDK 的 mock 测试、自动生成 case、anyproxy、正交表、pairwise、PICT

前言

最近和小伙伴们做了一件事,这件事从我提出这个想法、到执行、到完成的整个过程中,都充满了争议,包括团队内部、团队外部、我的内心,都充斥着各种不同的声音:“这件事价值不大”,“没有给手工测试减少多少工作量”,“弄的东西太虚” 等等。我也一度在怀疑这件事的价值,但是在这种情况下仍然坚持完成了,也取得了一定的收效。做完后,我咨询了一些比较有想法的同行前辈,有认同也有疑问,因此我想放到社区里听听更多更有想法的同行们的声音。如果能给大家带来一些启发,再好不过,如果不能,各位权当一个反面教材。

说在前头:

0.整这个东西是为了显得高大上,显得很牛逼么?
A:自动生成 case 一直是心里潜伏已久的想法,然而肚子里墨水有限,每天都在点点点,觉得不做点什么和咸鱼没区别。发出来不是来吹牛逼的,而是我知道:1)我们落地了想法;2)我们的思维是片面的,期望能抛砖引玉
1.针对的是单接口还是单业务场景?
A:我们 SDK 项目的特点之一:接口单一,没有复杂关联,最多就是某种返回会触发二次回传打点,因此非常适合做试点。而单业务场景可能会涉及很多接口的配合,比如登录场景
2. 接口测试用例的自动化生成?
A:我们没有自动生成 request 的各种请求参数,而是通过一些方法生成了 response 的 json 结果,用来 mock 测试 SDK 中各种异常处理以及打点回传数据,其目的之一是解决服务端开发同步太慢的问题,后面会介绍。
3.通过暴力组合来 fuzz,脱离了业务逻辑考虑,很多 case 是无意义的
A:两个问题:我们的做法不是通过暴力排列组合穷举,比如一个 int 字段,各种数字、特殊符合、空、空格、字母的输入来进行校验?不是的,我们会制定一个模版,模版的内容类似字段 adsType:[0,1,2,3,],~[a,null,空格,@#$%,...],其中前面的部分是正常的业务允许的取值范围,后面的负面测试数据,并非完全毫无目的的自由组合
4.复杂业务场景怎么办、关联接口依赖怎么处理、字段加密、签名校验、token、随机数组装
A:我们的 SDK 业务没有这些复杂关联,而字段加密,签名,随机数都可以在定制模版的时候完成
5.response 返回到客户端后,展示广告怎么测?下载、外开跳转、广告渲染怎么测?
A:我们大部分的设计是为了校验异常测试的,正常的功能测试依然是人工 + 部分自动化来实现
6.自动生成的 case 肯定有遗漏的地方
A:没错,但是比人工设计 case 覆盖要更全面,执行速度更快,更新速度更快,为什么可以,后面给大家介绍。

更多的问题我相信很快就有了,言归正传,开始介绍吧。

项目背景


广告 SDK 项目是为应用程序 APP 开发人员提供移动广告平台接入的 API 程序集合,其形态就是一个植入宿主 APP 的 jar 包。提供的功能主要有以下几点:

  • 为 APP 请求广告内容
  • 用户行为打点
  • 错误日志打点
  • 反作弊

团队现状


在项目推进的过程中,逐渐暴露了一些问题:

  1. 项目团队分为上海团队(服务端)和北京团队(客户端),由于信息同步,人力资源等其他原因,服务端与客户端的开发进度很难保持同步,经常出现客户端等着和服务端联调的情况
  2. 接口文档不稳定,理解有偏差
  3. 协议变化频繁,消息不同步
  4. 缺少服务端测试环境,可模拟的真实广告内容太少
  5. 协议字段太多,传统的测试用例设计方法容易出现遗漏,尤其是异常情况处理,测试完成以后测试人员对字段覆盖率没有信心
  6. 测试用例设计极容易受需求影响,更新起来非常麻烦,成本很高
  7. 手工测试方法执行效率低,且容易漏测

协议字段示例图

{
     "ads": [{
        "action": {  "path": "" },
        "adw":920, 
        "adh":900, 
        "template": "",
        "action_type": 2,
        "adid": "123123123",
        "adm": "",
        "adm_type": 0,
        "deep_link": "",
        "impid": "*******",
        "tk_act": [""],
        "tk_imp":[ ""],
        "tk_ad_close": [""],
        "tk_clk": [""],
        "tk_dl_begin": ["" ],
        "tk_dl_btn": [ ""],
        "tk_dl_done": [""],
        "tk_dp_suc": [],
        "tk_ins": [ ""],
         "tk_open": [""]
       }],
        "errno": "0"
}

手动测试过程示意图

分析&思路


上述几个问题,其中 1、2、3 都会对我们的测试工作产生影响,但是属于项目管理范畴,不在本文讨论范围内。那么针对 4、5、6、7 几个问题,应该如何解决呢?
首先分析一下问题:
Q: 缺少服务端测试环境,可模拟的真实广告内容太少
A: 由于服务端团队在人力上的不足,无法为我们提供测试环境,通过沟通协商,方法暂定为由服务端同事预先配置好线上广告物料,即固定的线上广告资源,能够覆盖提测的广告类型,在服务端完成功能逻辑之前,先利用 mock 方式测试客户端的功能逻辑以及展示,此时客户端和服务端后台无需交互。

Q: 协议字段太多,传统的测试用例设计方法容易出现遗漏,尤其是异常情况处理
A: 制定一个可靠的测试用例设计策略,以最少的 case 覆盖最多的情况。

Q:测试用例设计极容易受需求影响,变更起来非常麻烦,成本很高
A: 对测试用例进行拆分,分为正常返回情况和异常处理两部分。正常的处理包括系统环境、网络切换、下载、轮播、缓存、正常打点、安装卸载、UI 检查等等需要人工检查的情况,因此这部分我们先梳理 checklist,先组内 review,再约产品和研发一起 review,确保需求的完整性,另外开发过程中的需求变更是不可避免的,对于需求的变化要做到实时更新 case,这部分 case 覆盖的点要足够全,而文字描述要尽量的精简,确保更新起来能快速响应节奏的变化。而异常的部分,我们的做法是批量自动化生成 case,生成策略会在下面详细描述。

Q:手工测试方法效率低,且容易漏测
A: 正常的功能我们通过手工测试的方法覆盖,而对于客户端拿到的异常情况的 error code 要有全量的覆盖,比如我们的错误代码约定了 21 种,那么针对所有可能出现的错误代码都要想办法触发,这一部分工作希望从 case 生成到用例执行能 100% 的自动化实现。

调研过程


有了解决思路,那么需要想办法把想法落地。我们提炼出几个需要攻克的技术难点:

  • 难点一:mock 框架选型

做过单元测试的同学应该了解 “桩(stub)测试”,即通过 hard code 方式验证函数的输入输出是否准确和健壮,而 mock 测试和桩测试类似,功能要更加丰富一些,可以模拟产品交互环节中的部分场景,换句话说,可以让测试工作提前介入研发流程中。多用于需要联调的环节,比如支付场景,购买流程,第三方插件调用等等业务。之前我们采用的 Fiddler 重定向请求结果到本地文件的方式模拟服务端的 response 来欺骗客户端,也可以理解为 mock 测试。
最初我们计划自己写一个 proxy server 监听指定端口,截取所有的 http/https 请求,再替换 response 内容完成 mock 测试,后来一次偶然的机会接触了阿里开源出来的 anyproxy(http://anyproxy.io/cn/),了解了一下该工具,发现这款工具刚好满足了我们的几个需求:

  • 代码开源
  • 规则可定制
  • 支持 https 可视化
  • 易部署、学习成本低
  • UI 可视化,类似 fiddler

实际使用截图(我们对 response 展示做了点优化):

  • 难点二:可靠的测试用例设计策略

在讨论接口测试用例设计之前,我们需要预先圈定一个思考范围,以免过度的思维发散。结合我们的业务特征,由于 SDK 的功能大部分是单接口,少部分是关联接口,因此我们的设计基于单接口而非单个业务场景
接口的测试用例设计有别于其他测试用例,其业务逻辑主要体现在字段的取值上,每个取值体现了一种业务逻辑,我们做了一些调研,学习了其他业务团队的接口测试用例写法,发现测试人员喜欢这样设计 case:

这样的 case 无疑是工整、直观的,可读性比较强,很方便的复制粘贴,再通过修改其中的一个或者几个值,形成一个庞大的二维数组。
看到这个表格,一些熟练的测试工程师会立马联想到边界值、等价类设计、正交试验法等。然而要想保证每一个场景都被完整的覆盖,理论上我们需要测试所有字段的笛卡尔积,这种方式可以保证任何取值都会被覆盖到,但是当字段比较多的时候,测试用例的数量会呈爆炸式的增长,毫无疑问这种方式是不可行的。我们需要一个算法,能做到以下几点:
1、 以最少的组合覆盖尽可能多的场景
2 、覆盖所有字段的所有取值
3 、有统计学支撑,生成的数据有规律可循

有了需求,我们开始进行了可行性方案的研究,秉承不重复造轮子的理念,我们查阅了国内外很多的资料,逐渐的缩小了范围,在说出解决方案之前,先给大家简单介绍两个重要的算法:“ OATS(Orthogonal Array Testing Strategy)” 和 “Pairwise/All-Pairs Testing”,简称 “正交表法” 和 “配对测试法”。


正交表法

正交表法有两个重要的特性,大家尝试着理解一下:
1.每列中不同数字出现的次数相等

备注:这一特点表明每个因素的每个水平与其它因素的每个水平参与试验的几率是完全相同的,从而保证了在各个水平中最大限度地排除了其它因素水平的干扰,能有效地比较试验结果并找出最优的试验条件。

2.在任意两列其横向组成的数字对中,每种数字对出现的次数相等

备注:这个特点保证了试验点均匀地分散在因素与水平的完全组合之中,因此具有很强的代表性。

举个例子:有三个字段,每个字段可以取三个值,设字段表现为 A(A1,A2,A3)、B(B1,B2,B3)、C(C1,C2,C3),可以组成的集合恰好可以表现为一个三维空间图,如下图所示:

图中的正方体中每个字段的每个水平代表的是一个面,共九个面,任意两个字段的水平之间都存在交点,共 27(3x3x3)个,这就是笛卡尔积。按照两大特性设计出的正交表如右图所示,试验点用⊙表示。我们看到,在 9 个平面中每个平面上都恰好有三个点而每个平面的每行每列都有一个点,而且只有一个点,总共九个点。这样的试验方案,试验点的分布很均匀,试验次数也不多。
国外有一个网站能查询正交表的结果案例:http://www.york.ac.uk/depts/maths/tables/orthogonal.htm


配对测试法
配对测试法(Pairwise)是 L. L. Thurstone( 1887 – 1955) 在 1927 年首先提出来的。他是美国的一位心理统计学家。Pairwise 是基于数学统计和对传统的正交分析法进行优化后得到的产物。

定义:Most field faults were caused by either incorrect single values or by an interaction of pairs of values." If that's generally correct, we ought to focus our testing on the risk of single-mode and double-mode faults. We can get excellent coverage by choosing tests such that 1) each state of each variable is tested, and 2) each variable in each of its states is tested in a pair with every other variable in each of its states. This is called pairwise testing or all-pairs testing.

大概意思是:缺陷往往是由一个参数或两个参数的组合所导致的,那么我们选择比较好的测试组合的原则就是:
1)每个因子的水平值都能被测试到;
2)任意两个因子的各个水平值组合都能被测试到,这就叫配对测试法。
参看:http://www.developsense.com/pairwiseTesting.html

Pairwise 基于如下 2 个假设:

  1. 每一个维度都是正交的,即每一个维度互相都没有交集。
  2. 根据数学统计分析,73% 的缺陷(单因子是 35%,双因子是 38%)是由单因子或 2 个因子相互作用产生的。19% 的缺陷是由 3 个因子相互作用产生的。

因此,pairwise 基于覆盖所有 2 因子的交互作用产生的用例集合性价比最高而产生的。国外也有一份类似的数学统计:

我们通过一个订飞机票的实际例子来看一下,配对测试法是怎样从笛卡尔积中提炼出局部最优解的。
依然是三个字段的组合,分别是 Destination(Canada, Mexico, USA),Class(Coach, Business Class, First Class), Seat Preference(Aisle, Window),所对应的笛卡尔积共有 3x3x2=18 中测试组合,如下表所示。

经过配对测试法筛选后,结果如下:

经过筛选以后,我们的测试用例变成了 9 条,case 数量精简了 50%。简单总结 pairwise 的筛选原理就是,发现两两配对在全集中有重复的就去掉其中之一,这样筛选也有副作用,每次筛选完了条数是固定的,但是结果却不尽相同。但是通过上面的介绍我们不难比较出两种算法的差异。

说了那么多,再回到我们之前提到的设计策略几个需求,可以认为 pairwise 算法的特征基本满足了我们的需求。

  • 难点三:测试用例自动化生成

确定了用例设计的算法策略后,我们信心十足的准备开始设计我们的 response 返回值 case 了,我们套用文献中的排列分布方式应用到实际接口 json 中,悲伤的发现我们要组合的字段不是 3 个,而是 20-35 个左右,如果通过人工的方式来进行 case 设计的话,就算只考虑最多两个字段的值发生变化,数量也是非常惊人的,WWWWWWhat???

本着 “偷懒是人类进步的第一动力” 的想法,我们自然不会前功尽弃,自动化测试是我们的必选之路,接下来要做的就是调研目前已经存在的基于 pairwise 算法的工具有哪些,下面是经过调研后得到的工具列表。

基于 pairwise 算法的工具如此之多,那么相同模型设定下产生的结果是否存在差异呢?我们看一下这张图:
数据出自http:// www.pairwise.org/tools.asp

综合比较各工具产生的数据结果后,我们可以发现不同工具之间的结果差异并不大,基本上能够满足我们现有的需求。经过一番讨论后,我们决定采用微软的 PICT(https:\/\/github.com\/Microsoft\/pict)作为 case 生成工具,原因有几点:

1. 代码开源可扩展
2. 源码依然在维护,贡献比较活跃
3. 产品成熟,语法丰富
4. 基于贪心算法,局部最优解


难点四:测试用例生成的设计
用例生成过程分为五个步骤:

1. 准备字段值
根据 Wiki 的接口文档,测试人员理清字段结构,字段类型,字段取值范围后,结合传统的 case 设计理念,构造出每个字段的赋值,存放到整理好的 excel 中,大概是这样的:

有的同学可能会问:你这样整理也挺麻烦,感觉人工也没省多少事儿。这样设计的好处是,当字段发生变化的时候,只需要从源头修改字段属性、值、层级、甚至删除,后面整个流程中的 case 都会统一生效,字段集中管理,牵一发而动全身。和 UI 自动化用到的 page-object 设计类似。
2. 构建模型
有了面粉了,还需要加工一下才能变成我们想要的面包,我们需要把准备的数据整理成可以批量生成的可识别文件,即模型文件。PICT 的模型文件有自己的格式,类似这样:

参数定义
[子模型定义]
[约束定义]

举个例子,前面提到的订票系统的例子加工成模型文件是这样的,后面会给大家介绍语法含义:

Destination:  Canada, Mexico, USA
Class:  Coach, Business Class, First Class
Seat Preference:  Aisle, Window
{Destination, Class} @ 2

3. 生成 Case
通过 pairwise 工具将模型文件组装成我们想要的 case,那么上面的模型生成的 case 会是这样:

注:选择强度为 2,因此上面的矩阵是两两变化的。如前面所说,这里生成的矩阵内容不是固定的!

4. 准备期望结果
输入数据已经准备好了,那么相对于 case 而言,是不是还缺一个期望结果呢?在这里我们碰到了一个难题,可能做过 case 自动生成的同学都会遇到的,就是生成排列组合是非常简单的,如何让这些组合变得有意义,体现在我们的期望结果上,那么一次性生成如此多的 case,如何让输入值和期望结果对号入座呢?
我们的做法是:拆分了 postive testing 和 negative testing(合法输入测试和非法输入测试或负面测试),通过整理接口 case 我们不难发现,合法输入的 case 其实占整个 case 的比重并不大,工作量比较大的是各种参数的异常数据输入,相应的会产生 error code 或二次请求。只需要我们在整理数据的时候给出对应的 error code 即可,如图所示:

有的同学会问:我们协议还不稳定,error code 也不明确,有些输入也不知道对应什么 error code,怎么破?别急,后面告诉大家。

5. 生成 mock 数据
完成了以上准备工作以后,剩下的就是生成我们 mock 需要的 response json 数据了。解析 Wiki 协议中的 json 模版,给对应的 json 字段赋上生成的值,这里需要写一段代码来完成,在此不做赘述。

番外篇:工具的二次开发

在使用过程中,我们发现工具 PICT 不能满足业务场景的复杂度要求,主要有两点:

异常输入测试的时候,不能同时输入多个异常值

在 case 设计中多个异常值输入是很常见的测试场景,虽然 pict 提供负面测试(negative testing)功能,即如果模型文件中,有值被标记为异常值(默认的异常值标识符为 “~”),则 case 中会随机出现一个异常输入的值,但是 PICT 限制每个 case 只能有一个异常值存在,原因是多数异常值的组合虽然可能会引发问题,但是代码在 catch 了一个异常值造成的异常后,不会再去处理另一个异常值。
先通过一个示例来感受一下 pict 的负面测试。示例模型文件如下:

Destination:      Canada, Mexico, USA, ~Japan
Class:            Coach, Business Class, First Class
Seat Preference:  Aisle, Window, ~Door

产生的 case 如下:

通过上图可以看出,PICT 同时保证了正常值的组合,也保证了异常值的组合,但是我们不难发现,每个 case 只会出现一个异常值,那么 ~Japan,First Class, ~Door的 case 就会遗漏,显然 case 覆盖率不够,不能满足我们的需求。
针对这个问题,在对 PICT 的源代码进行了详细的解读后,我们对代码进行了二次开发,扩展了负面测试的覆盖范围,彻底解决了这个问题,修改后的模型文件如下:

Destination:      Canada, Mexico, USA, ~Japan
Class:            Coach, Business Class, First Class
Seat Preference:  Aisle, Window, ~Door
~{Destination, Seat Preference}  
增加一行公式在模型文件中指定了 Destination  Seat Preference两个字段可以进行异常值组合数量不限

扩展后的 case 生成是这样的:

正则表达式过于简单,不支持复杂的语句

PICT 支持 IF[ ] THEN[ ] 格式的约束规则。但是约束规则中 LIKE 关键字的通配符操作只支持 * 和?(分别表示任意多个字符和任意一个字符)。显然简单的通配符操作限制了约束规则的表达能力。因此,我们在原有的基础上,引入 C++ 的 regex 库支持正则表达式,修改后支持了更丰富的正则表达式。
如下示例中,增加一条规则,如果 Destination 字段为数字类型(”\d”),那么 Seat Preference 字段也为数字类型。

Destination:      Canada, Mexico, USA, 3
Class:            Coach, Business Class, First Class
Seat Preference:  Aisle, Window, 4
If [Destination] like "\d" then [Seat Preference] like "\d";

生成的 case 如下图:

有了强大的正则表达式,再加上多异常组合输入的支持,目前已经完全能覆盖我们需要的任何场景,向开源致敬!

共收到 16 条回复 时间 点赞

先赞一个~ 我们公司也在单接口简单场景的用例自动生成上尝试过,不过最终的效果一般……

对接口那里很感兴趣,希望详细介绍下

用例生成策略部分的想法挺好的~

simple #17 · 2016年10月19日 Author

#1 楼 @hub128 效果一般主要是哪方面效果呢?有没有改进的空间?

#4 楼 @simple 主要问题其实您的文章里都提到了,一方面是适用场景局限, 如果在场景/集成测试的自动化率不高的情况下,做这个并不能有效的降低测试人员的工作量。 所以只能作为补充手段。 另外一方面是用例生成策略方面,这部分我们完全没有做,你们的方案有挺多我们可以借鉴的地方的

#4 楼 @simple 方便留个微信或者 QQ 交流下不?

我看了 20 来分钟,还没有详细的抠楼主每一句话的意思,所以可能理解的不是很正确。楼主勿怪。

工具值不值得做

我先来聊聊这个问题吧,因为楼主一开始就提到了。 首先这个工具是有作用的,没作用的话楼主也不会研究这么个工具了。对于楼主的钻研精神还是点赞的。那么接下来我们谈谈资源控制,到底该不该做这个工具其实社区里没人能告诉你,因为我们都不太了解你们的产品,业务,研发流程,人力资源等等。我们在公司做事都是以结果为导向,所以在有限的资源下我们的做事方式是这样的:

  1. 首先列出所有要做的事情
  2. 为每个事情评估优先级 (可以按风险评估,可以按投入产出比评估)
  3. 把资源都投入到高优先级的事情上去

管理者都是上面的思维,他们要考虑当下最大的痛点是什么。当前需要多少人力来做业务测试 (手动) 产品质量才不会出问题。然后想的才是派几个人做自动化来慢慢减少手动测试的人力成本,再然后才是让人做工具的开发和创新,工具的开发也会排个优先级,哪个是最能减少人力成本的就先做哪个。在他们心里考虑的最多的是人力,是资源,说白了他要保证项目的平衡和稳定。 而工程师的想法就很简单了,觉得这个工具有挑战,能解决一些问题他就想搞,工程师的心里一把是没有什么资源,人力的概念的。 所以往往技术人和管理者在做一件事的时候总是会发生分歧。技术人有技术人的情怀和追求,管理者有管理者的现实和无奈。其实两边都没错,站的角度不同而已。我是比较倡导我们这些做技术的人要务实,因为有些时候真的可能捣鼓很长时间的东西,确实没怎么减少人力成本。主要优先级的问题没解决,倒是解决了一堆边边角角的问题,然后产品由于主要优先级的东西挂了。这时候其实很悲剧的。你很努力,但是从结果导向来说,大家加班的还是得加班,产品质量不好的还是不好。

所以不清楚楼主的团队情况很难定义到底值不值得做这么一个工具。这个要靠楼主自己来判断了。不过工具有用是有用的。这点没问题。

楼主面对的问题

我们来说一说楼主设计这个工具的初衷吧。主要是为了解决列出的这 7 个问题。我说说如果是我再不设计这个工具会怎么解决吧。

  1. 首先前三条:这个说实话靠个人力量改变不了什么。只能尽量推动 RD,产品和 QA 商量出一个好的流程。我承认有些公司的流程固化后很难改变的。人人都对变化有抵触情绪。但没办法,我们要尽量去推动流程的改善。其实这个才是高优先级问题。为什么前后端步调不一致?当初做需求和排期的时候为什么不能前后端统一步调?找出问题然后优化流程是从根本解决问题的思路。我知道这很难,但这是治本的方法。我们的团队已经找到了好的方式,把需求拆分的很合理,前后端不会出现互相等那么长时间的情况,虽不是无缝粘结,但也都是尽早联调,尽早提测,尽早发现问题,这是互联网人的思维。我们实际上发现即便联调前做好 mock server 的测试也是远远不行的。联调的时候问题百出,只有尽早联调尽早发现问题才能把风险控制下来。楼主做的是一个以技术弥补流程的东西,算是个无奈之举。楼主可以看一下 RAP 这个工具,前后端接口管理和 mock server 都有。
  2. 第四条,缺少服务端测试环境,可模拟的真实广告内容太少:为什么服务端会人手不足导致没有测试环境?我理解环境部署不应该是自动化的么? 为什么会有人力问题?楼主可以看一下我之前写的环境管理的文章,以 docker 为驱动,每个模块出个镜像就行了,我们连 hadoop 集群都能做镜像,广告应该也能把。当然我不了解楼主公司的情况。我只是提个解决思路。 我们的经验是一定不要让环境问题成为你的问题。如果环境没搞定,那就优先搞环境。
  3. 第五条,协议字段太多,传统的测试用例设计方法容易出现遗漏:从楼主的描述看,楼主倾向于把几乎所有参数组合都运行一遍,所以才想到自动生成 case。但是从软件开发的角度来看你们的软件不可能这么复杂的,不可能每一个参数组合都对应一个分支。楼主的工具在穷举一个很大的集合去调用接口。那么这种针对性比较弱的测试的效果先不说,其实可能比较好,只是我没有试验过,再一个就是运行时间问题,这么多的调用会运行多少时间?可能我不太了解楼主的 SDK 的接口是什么情况的,软件行业中很的接口调用都涉及到了数据库或者一些文件系统的操作。如果按楼主的思路大规模铺开的话,没准就是成千上万甚至更多的接口调用,如果运行时间过长,其实也是个问题。过多的 case 对于 bug 追踪也不是好事。因为一个接口的一个逻辑 bug 了,可能导致你数十数百的 case 失败。你很难判断他们是不是由于一个原因失败的,毕竟一个接口出现好几个 bug 的时候也不少见。所以其实既然已经到了接口级别的测试,那就已经是半个白盒了。我们做更有针对的测试是不是更好一点。通过阅读产品源码设计测试用例,做到有的放矢。 通过覆盖率工具佐证和辅佐 case 的补全不是很好么?QA 了解产品源码对于测试用例的设计是很有帮助的
  4. 第六条,测试用例设计极容易受需求影响,变更起来非常麻烦,成本很高:这个谁也避免不了。这一行的唯一不变的事就是需求永远在边。其实这条跟上面的一样,我比较喜欢有的放矢的测试用例。
  5. 第七条:手工测试方法效率低,且容易漏测:这个就尽量自动化么,没啥分歧的。

疑问

楼主做这个工具是为了 mock 服务端的 response 来验证 client 端,但是能自动生成 case 和验证返回值对吧? 我有点懵逼,文章里没太描述验证 client 端的东西。是只在 SDK 调用的时候验证 error_code 么?

总结

总的来说楼主的这个工具还是不错的,anyproxy 虽然我没用过,不过看描述它的 mock 功能还是比较强大的,用楼主的工具应该是可以找到人工测试很难找到的 bug 点,下面我列一点我觉得的问题,我们讨论一下

  1. 我们的验证点是否足够:由于我们的接口可能涉及到了很多数据库的读写,或者 HDFS 上的文件或者其他等等。我感觉楼主的工具是验证不了这么复杂的东西,仍然需要人手动,或者其他自动化方式去写 case 和逻辑验证。这个楼主有什么方案么
  2. bug 追踪问题:上面描述过。海量的 case 在 bug 追踪的时候怎么解决呢?假如 1 个接口自动生成了 200 个 case,其中失败了 100 个,error code 还是一样的。你能判断他们是由于一个原因失败的,还是多个原因失败的呢?
  3. 运行时间问题:上面也说过。不知道你们 SDK 接口的速度如何,我之前碰到的接口都有各种 IO 操作,速度着实不快。这么海量的跑着,会不会造成批量运行的接口特别慢。
  4. 节约资源和人力问题:这个上面说过我们做事的原则,我理解这个工具确实没节约资源。之前的测试该做还是得做,UI 自动化,接口自动化还是得搞。也就是说我个人觉得没有解决主要冲突~ 当然了楼主这个工具是可能会发现人工发现不了的 bug 的,毕竟自动生成这么多 case 的组合。但通过这个工具找到的其他人找不到的 bug 的严重级别有多重,我是有点疑问的,如果我们通过这个工具找到 bug 其实不太重要,但是理论上我们在传统的自动化之余又投入了人力去维护这样一个工具和这个工具带来的测试用例,机器资源开销,bug 追踪的人力成本等等。这个投入产出比,我们是不是要权衡一下呢。当然就像我一开始说的,也许楼主所在的项目已经把主要冲突都解决了。 楼主是完全可以搞这样的工具研发的。

不管怎么样,楼主的想法和分享精神是很好的

#6 楼 @hub128 我的 QQ 是 281653634,注明 testerhome,欢迎交流哈!

#7 楼 @ycwdaaaa 首先非常感谢那么认真的回复,写了那么多字呀!
我一个字一个字的看完的,就是希望跟坛友如此的探讨可行性。
我逐个答复一下上面的一些疑问哈,为了避免讨论变成各种 challenge,因为文章字实在太多,可能大家看的时候会有些跳跃。

工具值不值得投入
其实,我个人从这个课题的投入成本来看(1.5 个人,两周时间,即锻炼了新人,又给项目带来了一定的收益),是值得的。
我在社区里面贴出方案,其实是想听听不同的声音,顺便抛个砖看看能不能引出更好的、已经成熟的解决方案的 “玉” 来。并不是越做越迷茫,来求答案的,所以大家不要误会。


当前项目的问题优先级
其实做这件事的时候,我是有两方面考虑的。一方面是同事说每次发版一会感觉测试覆盖的场景不够,除了 case review 以外,对于字段的校验需要更多的检查(可能也是因为广告返回的字段太多让人产生了不踏实的感觉),因此我们需要更多的覆盖范围,而这个范围是可靠、可控的。另外一方面考虑的因素是,这种产品形态非常的时候做自动化,为什么不 尝试着解决一个现有的问题呢?还能锻炼成员,做一些有挑战性的事情。


关于技术人的情怀
其实就是 “我有一个锤子,想找个钉子” 的工程师思维,对吧?实际上我们解决问题的过程是这样的:团队开会列出最棘手的问题 -> 找出最短时间能解决的问题 -> 调研解决方案 -> 落地解决方案 -> 观察效果。


前三条问题
这个我们已经有流程上不错的方案了,只不过文章已经提到了,不属于技术讨论的范畴,流程的话可以单独拎出来说一下。RAP 我也有去看,之前调研的时候看了很多方案,在社区里翻了底朝天,但是这个产品太重,不太适合我们 SDK 的方案,所以没有去用。


环境问题
这个是历史原因,非常复杂,跨公司跨部门跨团队,而且领导层关注点不在这里,属于那种大家明知道有问题,却搞不定的困局,其中沟通的困难一时半会儿也讲不清楚的。


字段穷举
讲真的,如果真要是字段值的穷举法,我觉得我写那么一大坨也是白忙活了。今天有个朋友跟我说,我们的方案是从值的角度去设计 case,而不是业务角度去设计 case。怪我文章没讲清楚。我们在源头的模版定义的时候,会控制每个字段的取值范围,包括正常取值 [0,1,2,3,4] 这种有业务含义的取值,还有一种负面测试字段~[a,-1,null,@!#$] 类似这样的。而不是穷举一个字段所有的笛卡尔积再进行拼装。。。


运行时间
如文中所说,我们不到 3500 条 case 大概需要 75 分钟,因为手机分布式运行,数量还可以扩充。所以时间现在已经不是大问题了。另外我们不是穷举。。。


文件读写、数据库操作等
对的,接口很多是需要各种前置条件,修改 config,重启 service,清空缓存等等,如果这样的话,case 执行不知猴年马月了,而且还会带来很多不稳定因素。庆幸的是我们的 SDK 项目只需要校验前后字段的值,以及后期广告展示的效果(基于 UI 的),还有下载、跳转、打点等。


白盒、半白盒效率更高,case 设计有的放矢
赞同这个观点,我们在设计 case 的时候会考虑代码的条件分支走向,开发也会在我们运行结果后给出意见,来进行无效 case 的剔除,而我们要做的只是修改模版文件就可以。


错误的定位成本
这个疑问说到点子上了,过多的 case 设计未必是好事。定位错误的成本会越来越高,我们确实存在这个问题。方案是 case 的值变化比较明显,开发可以很快的判断这个或这一类的场景是否需要检查或 catch,成本略高,还没想到更好的方案


尽量自动化
如你所看到的,正在尽可能的多做自动化。。。


Client 端难道啥也不干,只校验 error code?
Client 端不是啥也不干,是后面的广告渲染、二次跳转、下载 APP、内开外开跳转、事件打点等等操作已经不能用我们这个方案来做了,此时用到了手工测试,考虑到复用性的问题,GUI 的自动化暂时还不打算引入。


最后,再次感谢那么认真的答复!

#4 楼 @simple 关于 case 穷举问题,是我理解有些偏差。而且既然你们的 SDK 不依赖数据库,文件系统,缓存等设施,真的是太适合做自动化了。很多问题确实已经不是问题了。 那么咱俩讨论一下目前最大的痛点:bug 的定位吧。

  1. 我们之前干过这样一件事,report 中可以选择失败的 case 并重跑,重跑的时候代码覆盖率工具走起,举个例子假如你有一个接口的 N 个 case 失败了。就选取这个接口失败的 case 重跑并统计代码覆盖。我们看看是在哪一行或哪几行抛的异常。
  2. 我想你们 SDK 应该有日志吧,你可以监控日志的关键字:例如 error,exception。并截取出错误信息显示在 report 里以辅助 bug 定位。当然这个比较依赖 RD 的日志规范。

我暂时就能想到这两个办法了。

#9 楼 @simple 对了还有个问题,不知道你碰见过没有。 就是有些参数之间是有逻辑关联的。 例如如果参数 A 取值为 x,那么参数 B 必须从 1,2,3 中取值。如果参数 A 取值为 y,那么参数 B 必须从 4,5,6 中取值。不知道我表达的清楚么。我现在的产品中不少接口都是这样的。碰到这种接口的话,自动化生成 case 的规则就变的复杂了。

simple #12 · 2016年10月20日 Author

#11 楼 @ycwdaaaa 接口的代码覆盖率工具,求推荐一款?我觉得可以试一下!
关联参数问题,我们可以解决,在生成模型文件的时候,可以写逻辑关系:if(adsTpye in [0]) then(deeplink in [a,b,c]),这样的话生成的 case 会限制在 adsType=0,deeplink=a; adsType=0,deeplink=b; adsType=0,deeplink=c;的范围内😁

#12 楼 @simple 我只用 java 的覆盖率工具,jacoco,推荐 jacoco

非常感谢作者的思路,让我醍醐灌顶~

感谢作者的分享,非常赞同偷懒是进步的第一动力

simple 接口测试覆盖率设计讨论 中提及了此贴 03月03日 09:11
simple 测试用例设计之正交表法详解 中提及了此贴 11月13日 11:56
simple 多重条件下测试用例如何设计? 中提及了此贴 11月12日 10:03

一个接口比如有 20 个参数,其中有 5 个参数是需要关联它的前置用例接口获取的值,这 15 个关联的值就不能做为参数的组合,剩下的 5 个参数才能进行组合吧。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册