从我第一次在咱们社区发帖子的时候我就说过我的测试思路跟很多人不一样,导致于其实我跟很多人都有过冲突。其中一点就表现在我对自动化测试脚本的质量的执着。那么今天,就让我来说一点我自己的想法吧。可能跟大多数人想的不一样,请轻喷。
自动化测试脚本的质量有很多方面,我今天直说我觉得最重要的几个方面:
我们先说这一点吧,为什么我们的脚本要是互不影响的,隔离的呢。因为我们所有的脚本都是运行在同一个环境的,每个脚本可能都会在这个环境上造成痕迹,这些痕迹可能是数据库的,可能是文件系统的。而这些痕迹造成了 case 之间互相的影响。举个最常见的例子,本来我们运行的脚本们都很成功,没有什么异常。但是突然有有一天,测试删除数据的脚本由于产品的 bug 失败了,数据没有真正的被删除。于是乎间接的造成了之后的查询所有数据的脚本失败了。因为之前的脚本没有删除掉数据,现在预期值和返回值不一样了----多了一条数据。这种情况发生在很多场景,可能是 case 中设置了产品的全局变量,语言的信息等等。下面我们说解决方案。
下面我们分别说说吧,我把第一点称作我最不推崇的,虽然它看起来貌似没什么实现成本。但是这种纯靠所有 QA 之间的约定和个人的自觉其实很不靠谱。即便我之前在外企的时候有严格的 code review 流程也很难保证 case 之间互不影响,更别说国内的测试我基本就没怎么见过有 code review 这个流程。case 数量变多的时候,就慢慢的发现以前埋的坑有多大,很典型的情况就是,测试用例在单独跑的时候怎么跑怎么过。放到 server 上所有 case 一起跑的时候怎么跑怎么不过。这时候你还不知道到底是哪个 case 造成的痕迹导致你的脚本跑不过去的。一出现这种情况一般没个半天一天的根本查不出来到底是以前哪个 case 造成的痕迹破坏了你的脚本。当然了,如果你说你们项目一共也就一百两百的 case 量,那就当我没说吧。这种方式很好。
第二种方式我比较推崇,很黄很暴力。绝对杜绝了任何互相影响的可能性。成本比较高,但是可以从框架的角度解决这个问题,这样成本就降低到了一个可以接受的程度了。具体实现可以参考我以前的两个帖子,如下:
框架中测试数据的管理策略
框架中测试数据的恢复策略
很多人可能会喷我的这个观点,仁者见仁智者见智吧。UI 自动化和接口自动化我都用这种方式。
我把这两点写在一块了,因为解决这两点的方案是一样的。我们以前一定碰见过很多次这样的情况,由于下单这个功能 bug 了,导致所有依赖下单这个功能的脚本全都失败了。为什么呢?因为用例都是流程性的测试,都是先下单,再查询订单,再更新订单,再评论订单等等。我们把相似的功能都放在数个脚本里测试了。这样的结果有两个,第一是可能因为一个 bug 导致 N 个脚本的 fail。你需要挨个排查很影响效率。第二是以 QA 的能力很难定位 bug 到底出现在哪。举例说明一下,假如脚本在查询订单的时候失败了,你能确定一定是查询订单这个 feature 出 bug 了么?不一定吧,可能下单的时候就没入库正确。当然了我只是举个最简单的例子,下单和查询其实还是很容易定位的,但实际情况很复杂。很可能入库的时候的错误在脚本最后的某个功能上才体现出来。例如运行某些 task,需要在入库时的一些元数据等等。
解决方案----就是减少流程性的测试。把被测的产品功能打碎了,切成一块一块的个体。一个脚本中只测试一个 feature。杜绝了互相影响的同时也简化的 bug 定位的成本,因为你脚本里只测试了这个 feature,失败了就肯定是这个 feature 的问题了。只要你代码能力还行,直接去看产品源码就能找到 bug 到底出现在哪一行代码中了,我用这种方式 fix 过很多个 bug。有些同学一定会问,这不可能实现,因为测试当前功能一定要其他功能创建数据啊?那到底怎么做呢? 方案也很简单--直接在数据库,文件系统,索引等等中插入数据,不使用产品本身的 feature 创建数据,我们仔细想想,除了单元测试之外,我们的这些功能之间真的有依赖么? 答案是没有,功能依赖的是数据,是数据库中的数据。你再 UI 上走一遍流程也就是在数据库中创建了一系列的数据而已。so,如果有同学疑虑说没有流程性测试其实是不完整的测试的话。那你大可不必担心。因为这跟流程性测试根本没区别,有什么流程性测试的数据是你用 sql 模仿不了的?所以有区别的只是造数据的方式而已。这种方式的脚本成本一般也比较高,因为要清楚产品数据库的结构,要写 sql。但是我们同样可以在框架上解决这个问题,同样看我上一点发的两个链接。里面有提及。
这个比较容易懂,就是你得能让人很简单的就看明白你的脚本都在干什么,验证什么。《xunit test parttern》一书中推崇测试脚本可以当成产品文档一样阅读。开发没时间写文档?那么代码就是文档,测试脚本就是文档。举个例子吧
Cookie cookie= this.login("user","password");
//String temp =
given()
.cookie(cookie)
.contentType("application/json")
.body(project)
.expect()
.statusCode(200)
.body("status", equalTo(0))
.body("msg", equalTo("ok"))
.when()
.post("/projects/380").asString();
Request request = new Request(dataSource,"select * from project where id = 380");
assertThat(request).row()
.value("name").isEqualTo(project.getName())
.value("description").isEqualTo(project.getDescription());
上面的脚本我们很容易看出来我们访问了/projects/380 这个 API,传递了一个 project 对象当做参数,反正了各种返回值后并到数据库中验证了 project 的 name 和 description 的属性。这个依赖于 QA 良好的编码规范以及测试框架的支持。有兴趣的看我之前发的关于可毒性,可维护性,可扩展性的那篇帖子吧。
这一点我也得好好的说明一下,因为现在关键字驱动和录制回放式框架的盛行。让人很容易陷入一种幻觉,那就是不会写代码的 QA 用这种工具就可能完全代替写代码了。这种测试框架也变成了一种高大上的证明。完成了这种框架十分有利于你的 KPI,装逼首选方案(实际上我也这么干过)。之前迫于无奈,我上一家公司实在没几个 QA 会写代码,我只能推行关键字驱动了。领导觉得很牛逼,很高大上,各种到处吹嘘,最后吹的我自己都不好意思了。但实际上关键字驱动的效率,我心里清楚的很。极其的不灵活,极其的不利于扩展,你想加什么功能,除了实现以外你还得想着怎么设计关键字能让不会写代码的 QA 能接受。到了最后为了实现各种各样丰富的功能以满足测试需要后。发现特么的关键字的语法都很复杂了。这特么的比他们学写脚本好不了多少。而且没有 debug 能力的 QA 搞自动化本身就不靠谱。会写代码的 QA 也极其反感这种框架,写时间长了直接就是离职的节奏。录制回放式的更不用说了,直接无法满足我上面说的几个提升脚本质量的点,再一个录制回访式的框架录下来的脚本。。。我就不说了。。。坑爹 +1. 所以我一直觉得测试这行有个误区。那就是以不让其他 QA 写代码为荣耀,却忽略了脚本本身的个质量。我觉得不能无限制的为了降低学习成本而牺牲质量和效率,还是想想办法怎么提升团队的技术水平吧。
OK,说了这么多吐槽的话,我一直没说怎么做?我也不想说了,因为太多了。我的整个《测试开发之路》系列一直在说这个问题。有兴趣的就去看看我以前的帖子吧。
其实脚本质量还有其他方面,我在这就不说了,要不又该长篇大论了。我就跟个老头似的,啰嗦起来没完没了。最后说点感慨吧。工作快 5 年多了。从以前安安静静的写自动化脚本,到为了那种关键字驱动而废寝忘食,再到追求各种能装逼的架构和平台,最后才到现在又回归到平平淡淡的写脚本,我领悟到了一件事:不要为了各种花哨的东西丢失了最基本的东西。还是那句话:前路走好,勿忘初心