最近几年,前端测试渐渐被人重视,相关的框架和方法已经比较成熟。
断言库有should, expect, chai。 单元测试框架有mocha, jasmine, Qunit。 模拟浏览器测试环境有Phantomjs, Slimerjs。 集成测试任务管理工具备karma。此外,还有一堆诸如Selenium、nightwatch(冰火出戏)等各色思路不一样的关注UI测试的工具。
本文主要关注的是接口测试。
接口是先后端协做的桥梁,是系统得以顺利运行的关键。所谓接口测试,就是检查系统提供的接口是否符合事先撰写的接口文档。数据结构是否完备,数据类型和取值是否符合标准。
实际上,接口测试由后端来作会比较方便,可是因为一些实际缘由(后端大哥比较忙,人手不足等),后端每每不能确保接口数据必定符合接口文档规定。此时,做为前端,咱们为何不能另辟蹊径,探索一番呢?css
接口测试最大的难点是什么?我以为是登陆状态的获取。
众所周知,一个系统的大部分接口都只会对登陆用户开放。在测试接口以前,首先必须确保此次会话已经登陆,发送给后台的请求中必须携带登陆后获取的 cookie。面对这个问题,最直接的想法是在后台模拟登陆。html
mocha 能够运行在node中,理论上咱们彻底能够在node环境下,使用一些http client( request, superagent等),模拟进行登陆。手动获取cookie,并加在以后的每一次接口请求中。得到到接口数据后,就能够结合 mocha,愉快地进行测试了。
但是,实际实验以后,我发现这个方案存在一些缺陷。前端
难以封装复用
不一样系统的登陆流程并不相同。至关多的系统要求在登陆时须要附带其余的 cookie 或者额外的表单参数。
以本人测试的系统为例,登陆时就须要带上一个 key 为 jsessionid 的 cookie,以及一个 key 为 nlt 的表单参数。这个 cookie 是怎么来的呢?是访问登陆页时返回的 set-cookie 头设置的。这个表单参数又是怎么来的呢?是藏在登陆页面的表单中的一个隐藏域。
因此,若是简单地用 http client 向登陆地址发一个 post 请求,仅仅带上本身的用户名密码,你就会发现,登陆毫无疑问的···失败了。
更烦人的是,若是你的系统登陆依赖 SSO,内部存在 302 请求转发,那么颇有可能会出现 cookie 丢失的状况(有两个 set-cookie header,浏览器能识别并正确设置,可是不少 http client 只能拿到最后一个)。
咱们固然能够动用各类奇技淫巧,手动获取和设置各类特定的 cookie,去网页里爬出隐藏域的值。但是,这一切,仅仅对特定的系统有效。若是换一套系统,你就必须从新研究一遍登陆流程,从新写一遍模拟登录代码,感受仍是挺崩溃的。node
难以应对验证码
上面的问题仅仅只是使用不便,这个问题就是直接就给该方案判了死刑。固然,爬虫界也有很多应对验证码的解决方案,好比依托 OCR 软件啊,人工判别 API 啦, 更厉害的还有本身作图像处理和算法进行识别。但是,做为一个简单的接口测试,这种方案是否是过小题大作了点?webpack
mocha 也能够在浏览器环境运行,当后台模拟登录遇到困难,咱们很容易想到,在浏览器环境进行测试是否可行呢?
普通的浏览器环境确实会有必定问题,那就是前端的老大难:跨域。
咱们的目的是前端接口测试,原则上不该也不能让后台搭配测试工做去改接口,因此,CORS,jsonp 都是不可行的。
难道这条路又堵死了?非也,咱们还有一个选项,那就是——浏览器扩展。
以 chrome extension 为例,只要在 manifest.json 文件中配置好 permissions ,扩展发起的请求就能够成功跨域。事实证实,彻底没有问题。不只能够跨域,还能够携带登陆后的 cookie,因此,只要在浏览器正常登陆后,再打开插件相关页面进行验证,就能够解决登陆状态的问题。简直完美。web
既然定下了方案,下一步就是设计。做为接口测试的解(wa)决(keng)方(zhi)案(lv),咱们必须具有通用性与易用性。使用者只需提供配置,不须要再进入源代码修改打包。
本方案中中测试框架和断言使用 mocha + chai。这方面的资料汗牛充栋,本篇再也不赘言。算法
apiTest │ package.json │ webpack.conf.js │ api.conf.dist.js │ api.conf.dist.js.map │ index.js │ manifest.json │ test.png ├─config │ index.js ├─css ├─html ├─js ├─lib └─TestCreator
其中,index.js 是入口文件; manifest.json 是 chrome 插件配置文件; config/index.js 是用户测试配置文件。css, html, js, lib 都用于存放资源文件。chrome
安装插件(若是已安装过就能够省略)npm
提供测试配置json
用户去系统登陆页完成登陆
点击插件图标,打开新 tab 页
在 tab 页中进行测试,并在页面上显示结果
让咱们从 manifest.json 文件开始,一步一步深刻,看目标流程是如何实现的。
- manifest.json
"permissions": [ "tabs", "http://*/*", "https://*/*" ], "background": { "scripts": "js/background.js" }
在 manifest.json 文件中,定义 background 属性。定义的 js/background.js 文件将自动在后台运行。permissions 属性定义了扩展的权限。“tabs”指明能够打开浏览器自己的 tab 标签页,后面两个配置指明浏览器能够向任何 url 发送请求。(不配置会跨域)
- js/background.js
chrome.browserAction.onClicked.addListener(function(){ var url = chrome.extension.getURL("../html/main.html"); window.open(url, "main_page"); })
在 js/background.js 文件中,注册了一个事件函数。当用户点击插件图标时,打开一个 html/main.html 做为新的 tab 页。咱们全部的任务都将在这个 tab 页中实现。
- html/main.html
<!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" href="../css/demo.css"> <link rel="stylesheet" type="text/css" href="../css/mocha.css"> <script src="../lib/mocha.js"></script> <script src="../config/index.js"></script> <script src="../api.conf.dist.js"></script> </head> <body> <div id="ext-box"> <button id="startBtn">请在登录后点击按钮开始测试</butotn> </div> <div id="mocha"></div> </body> </html>
在这个 html 中,载入了三个 js 文件。其中,config/index.js 是测试配置文件。api.conf.dist.js 是入口文件经 webpack 打包后的压缩文件。mocha 之因此独立载入,是由于在浏览器环境中,mocha 必须部署在 window 对象下,因此不能打包进去。核心的逻辑代码,放在 api.conf.dist.js 文件中。
页面中放置了一个按钮和一个 id 为 mocha 的 div。前者点击后会触发 mocha 执行,后者做为一个容器,用于显示测试结果。
- index.js
import {expect} from 'chai' import test from './js/main' function click(e) { mocha.run() document.querySelector("#startBtn").style.display = 'none'; } document.addEventListener('DOMContentLoaded', function () { var btn = document.querySelector("#startBtn") btn.addEventListener('click', click); }); mocha && mocha.setup('bdd') mocha.timeout(4000) const mainTest = testCreator(window.apiTestConf) mainTest()
index.js 是打包的入口文件。这里主要作了两件事:
第一,为按钮定义了一个事件监听函数,点击时执行 mocha。
第二,初始化 mocha,设置其执行模式(bdd)和超时时间。
第三,执行 mainTest 函数,在这里定义了全部测试用例。测试用例根据用户配置文件动态,这也是核心逻辑。下面将进一步详述。
下面展现本文使用的测试配置:
window.apiTestConf = { name:"foo", path:"http://baz/japi/platform/", common:{ success: { value: true, type:["boolean"], }, errorCode: { value: [0] } }, publicStruc:[ "results>items", "results" ], apis:{ AccountCenter:{ "110620001":{ name:"获收货地址列表", fullUrl:false, data:{ length:{ min:1 }, item:{ area:{ type:"string" }, areaStr:"string", consignee:"string", detail:"string", id:"number", ifDefault:"boolean", mobile:["string","number"] } } } } } }
测试配置决定了测试用例的组织和测试用例内断言的编写。其结构学习了一些表单验证插件,比表单验证插件复杂的地方在于,表单验证仅仅针对一维的单一字段,而接口返回的数据则是具有必定的嵌套结构的。下面对该配置进行简述。
path:接口地址的公共部分。
common:对接口返回数据的格式上的公共部分进行验证。好比 success 必须为 true,errorcode 必须为 0 等。
publicStruc:接口核心数据部分的路径。系统将根据该路径一步步寻找。若是没找到将会在断言中报错。若是定义了多个路径,则会按顺序从前日后查找。
apis:核心部分,对接口数据进行验证。
AccountCenter:该 key 可变。在这一层级对接口进行分组,好比 AccountCenter 说明下面定义的是帐户中心的接口的测试配置。
110620001:该 key 可变。这是真实接口的路径。该 key 值下的对象,就是针对具体数据结构和字段进行配置了。下面将介绍这部分的配置和验证规则,可能会有点枯燥,不感兴趣的读者能够略过不看。
字段验证规则:
字段验证规则指的是接口下data属性下定义的规则。是对接口核心数据的存在与否、数据类型和值所作的规定
基本规则规则
规则仅仅对类型是数字,字符串,布尔值和数组的字段有效,若是要验证的字段是对象,则不起做用。须要对对象内的属性(一样看作字段)作进一步的验证,而不是对对象自己作验证。
验证属性可写的值是对象,数组和字符串,其余类型的值都是非法的
验证属性若是是一个空对象,则直接经过。
验证属性若是是一个数组或字符串,则视为 type 处理
验证属性若是是一个至少包含了required, value, type三者之一的对象,则进行标准化验证
required
验证真实字段必须指定,且必须不为空字符串(也就是说false,0能经过验证。)
value
若是 value 是数组,则验证真实值是否至少等于数组中的某一个值
若是 value 是数字,字符串或者布尔值,验证真实值与其是否相等
若是 value 是一个对象,则进一步判断:
min:规定最小值,仅仅对数字有效
max:规定最大值,仅仅对数字有效
not:不为其值。能够是一个数组,此时不为数组中的全部值
start:开始字符,仅仅对字符串有效
end:结束字符串,仅仅对字符串有效
type
type 可写的值是字符串。这些字符串的可选值为:"number","string","boolean","array"。没有"object","undefined","null",由于没有意义
type 能够是一个数组,数组中的值也是上面的可选值。此时,只要知足数组中的一个值,就能经过验证
length
当数据是数组时才有效,是对数组长度作的验证
min:规定最小值,仅仅对数字有效
max:规定最大值,仅仅对数字有效
item
当数据是数组时才有效,里面进一步规定对数组中每一项的验证
设计完各类规则后,接下来就是依样画葫芦地编写代码了。因为代码量比较多,这里就再也不赘述。其核心思想就是根据配置项生成各类测试用例和断言。最后供 mocha 运行,输出结果。
本文所设计的方案基本可以知足前端接口测试的要求,但仍是有一些缺陷,弊端,及有待改进的地方。
验证规则涉及不够全面和严密,有待丰富,
能够将整体目录结构以及内部的 TestCreater 封装成 npm 包,更方便使用。
测试经过时,没法看到经过了哪些断言。测试失败时,只输出未经过的第一条断言的信息。输出信息不够全面丰富。这必定程度上是 mocha 和 chai自己的特性,不知道有什么方式能够优化。
最后,本文中若出现了错误,或是读者有更好的方案,望不吝啬赐教。