当前市面上存在的接口测试工具已经非常多,常见的如 Postman 、 JMeter 、 RobotFramework 等,相信大多数测试人员都有使用过,至少从接触到的大多数简历的描述上看是这样的。除了这些成熟的工具,也有很多有一定技术能力的测试(开发)人员自行开发了一些接口测试框架,质量也是参差不齐。
  但是,当我打算在项目组中推行接口自动化测试时,搜罗了一圈,也没有找到一款特别满意的工具或框架,总是与理想中的构想存在一定的差距。
  那么理想中的接口自动化测试框架应该是怎样的呢?
  测试工具(框架)脱离业务使用场景都是耍流氓!所以我们不妨先来看下日常工作中的一些常见场景。
  ●测试或开发人员在定位问题的时候,想调用某个接口查看其是否响应正常;
  ●测试人员在手工测试某个功能点的时候,需要一个订单号,而这个订单号可以通过顺序调用多个接口实现下单流程;
  ●测试人员在开始版本功能测试之前,可以先检测下系统的所有接口是否工作正常,确保接口正常后再开始手工测试;
  ●开发人员在提交代码前需要检测下新代码是否对系统的已有接口产生影响;
  ●项目组需要每天定时检测下测试环境所有接口的工作情况,确保当天的提交代码没有对主干分支的代码造成破坏;
  ●项目组需要定时(30分钟)检测下生产环境所有接口的工作情况,以便及时发现生产环境服务不可用的情况;
  ●项目组需要不定期对核心业务场景进行性能测试,期望能减少人力投入,直接复用接口测试中的工作成果。
  可以看到,以上罗列的场景大家应该都很熟悉,这都是我们在日常工作中经常需要去做的事情。但是在没有一款合适工具的情况下,效率往往十分低下,或者是某些重要工作压根没有开展,例如接口回归测试、线上接口监控等。
  先说下简单的手工调用接口测试。可能有人会说, Postman 可以满足需求啊。的确, Postman 作为一款通用的接口测试工具,它可以构造接口请求,查看接口响应,从这个层面上来说,它是满足了接口测试的功能需求。但是在具体的项目中,使用 Postman 并不是那么高效。
  不妨举个常见的例子。
  某个接口的请求参数非常多,并且接口请求要求有 MD5 签名校验;签名的方式为在Headers中包含一个 sign 参数,该参数值通过对 URL 、 Method 、 Body 的拼接字符串进行 MD5 计算后得到。
  回想下我们要对这个接口进行测试时是怎么做的。首先,我们需要先参照接口文档的描述,手工填写完所有接口参数;然后,按照签名校验方式,对所有参数值进行拼接得到一个字符串,在另一个MD5计算工具计算得到其MD5值,将签名值填入 sign 参数;后,才是发起接口请求,查看接口响应,并人工检测响应是否正常。坑爹的是,我们每次需要调用这个接口的时候,以上工作得重新来一遍。这样的实际结果是,面对参数较多或者需要签名验证的接口时,测试人员可能会选择忽略不进行接口测试。
  除了单个接口的调用,很多时候我们也需要组合多个接口进行调用。例如测试人员在测试物流系统时,经常需要一个特定组合条件下生成的订单号。而由于订单号关联的业务较多,很难直接在数据库中生成,因此当前业务测试人员普遍采取的做法,是每次需要订单号时模拟下单流程,顺序调用多个相应的接口来生成需要的订单号。可以想象,在手工调用单个接口都如此麻烦的情况下,每次都要手工调用多个接口会有多么的费时费力。
  再说下接口自动化调用测试。这一块儿大多接口测试框架都支持,普遍的做法是通过代码编写接口测试用例,或者采用数据驱动的方式,然后在支持命令行(CLI)调用的情况下,可以结合 Jenkins 或者 crontab 实现持续集成,或者定时接口监控的功能。
  思路是没有问题的,问题在于实际项目中的推动落实情况。要说自动化测试用例靠谱的维护方式,还是直接通过代码编写测试用例,可靠且不失灵活性,这也是很多经历过惨痛教训的老手的感悟,甚至网络上还出现了一些反测试框架的言论。但问题在于项目中的测试人员并不是都会写代码,也不是对其强制要求能马上学会的。这种情况下,要想在具体项目中推动接口自动化测试很难,算我可以帮忙写一部分,但是很多时候接口测试用例也是要结合业务逻辑场景的,我也的确是没法在这方面投入太多时间,毕竟对接的项目实在太多。所以也是基于这类原因,很多测试框架提倡采用数据驱动的方式,将业务测试用例和执行代码分离。不过由于很多时候业务场景比较复杂,大多数框架测试用例模板引擎的表达能力不足,很难采用简洁的方式对测试场景进行描述,从而也没法很好地得到推广使用。
  可以列举的问题还有很多,这些也的确都是在互联网企业的日常测试工作中真实存在的痛点。
  基于以上背景,我产生了开发 ApiTestEngine 的想法。
  对于 ApiTestEngine 的定位,与其说它是一个工具或框架,它更多的应该是一套接口自动化测试的佳工程实践,而 简洁优雅实用 应该是它核心的特点。
  当然,每位工程师对 佳工程实践 的理念或多或少都会存在一些差异,也希望大家能多多交流,在思维的碰撞中共同进步。
  核心特性
  ApiTestEngine 的核心特性概述如下:
  支持API接口的多种请求方法,包括 GET/POST/HEAD/PUT/DELETE 等
  测试用例与代码分离,测试用例维护方式简洁优雅,支持 YAML
  测试用例描述方式具有表现力,可采用简洁的方式描述输入参数和预期输出结果
  接口测试用例具有可复用性,便于创建复杂测试场景
  测试执行方式简单灵活,支持单接口调用测试、批量接口调用测试、定时任务执行测试
  测试结果统计报告简洁清晰,附带详尽日志记录,包括接口请求耗时、请求响应数据等
  身兼多职,同时实现接口管理、接口自动化测试、接口性能测试(结合Locust)
  具有可扩展性,便于扩展实现Web平台化
  特性拆解介绍
  支持API接口的多种请求方法,包括 GET/POST/HEAD/PUT/DELETE 等
  个人偏好,编程语言选择Python。而采用Python实现HTTP请求,好的方式是采用 Requests 库了,简洁优雅,功能强大。
  测试用例与代码分离,测试用例维护方式简洁优雅,支持 YAML
  要实现测试用例与代码的分离,好的做法是做一个测试用例加载引擎和一个测试用例执行引擎,这也是之前在做 AppiumBooster 框架的时候总结出来的优雅的实现方式。当然,这里需要事先对测试用例制定一个标准的数据结构规范,作为测试用例加载引擎和测试用例执行引擎的桥梁。
  需要说明的是,测试用例数据结构必须包含接口测试用例完备的信息要素,包括接口请求的信息内容(URL、Headers、Method等参数),以及预期的接口请求响应结果(StatusCode、ResponseHeaders、ResponseContent)。
  这样做的好处在于,不管测试用例采用什么形式进行描述( YAML 、JSON、CSV、Excel、XML等),也不管测试用例是否采用了业务分层的组织思想,只要在测试用例加载引擎中实现对应的转换器,都可以将业务测试用例转换为标准的测试用例数据结构。而对于测试用例执行引擎而言,它无需关注测试用例的具体描述形式,只需要从标准的测试用例数据结构中获取到测试用例信息要素,包括接口请求信息和预期接口响应信息,然后构造并发起HTTP请求,再将HTTP请求的响应结果与预期结果进行对比判断即可。
  至于为什么明确说明支持 YAML ,这是因为个人认为这是佳的测试用例描述方式,表达简洁不累赘,同时也能包含非常丰富的信息。当然,这只是个人喜好,如果喜欢采用别的方式,只需要扩展实现对应的转换器即可。
  测试用例描述方式具有表现力,可采用简洁的方式描述输入参数和预期输出结果
  测试用例与框架代码分离以后,对业务逻辑测试场景的描述重任落在测试用例上了。比如我们选择采用 YAML 来描述测试用例,那么我们应该能在 YAML 中描述各种复杂的业务场景。
  那么怎么理解这个“表现力”呢?
  简单的参数值传参应该都容易理解,我们举几个相对复杂但又比较常见的例子。
  接口请求参数中要包含当前的时间戳;
  接口请求参数中要包含一个16位的随机字符串;
  接口请求参数中包含签名校验,需要对多个请求参数进行拼接后取md5值;
  接口响应头(Headers)中要包含一个 X-ATE-V 头域,并且需要判断该值是否大于100;
  接口响应结果中包含一个字符串,需要校验字符串中是否包含10位长度的订单号;
  接口响应结果为一个多层嵌套的json结构体,需要判断某一层的某一个元素值是否为True。
  可以看出,以上几个例子都是没法直接在测试用例里面描述参数值的。如果是采用Python脚本来编写测试用例还好解决,只需要通过Python函数实现即可。但是现在测试用例和框架代码分离了,我们没法在 YAML 里面执行Python函数,这该怎么办呢?
  答案是,定义函数转义符,实现自定义模板。
  这种做法其实也不难理解,也算是模板语言通用的方式。例如,我们将 ${} 定义为转义符,那么在 {} 内的内容不再当做是普通的字符串,而应该转义为变量值,或者执行函数得到实际结果。当然,这个需要我们在测试用例执行引擎进行适配实现,简单方式是提取出 ${} 中的字符串,通过 eval 计算得到表达式的值。如果要实现更复杂的功能,我们也可以将接口测试中常用的一些功能封装为一套关键字,然后在编写测试用例的时候使用这些关键字。