传统的做法,UI 自动化、接口测试、性能测试、健壮性测试.....等各种测试分而治之、测试结果也没有建立起内在关联性。B2C 产品的快速迭代,留给测试的时间不多,如何在自动化层面提升执行效率、提高定位问题效率一直存在痛点。
我一直在想,如何分层并行、多类型测试并行、不同类型的测试结果建立内在联系,这至少是解决以上痛点的一个可行性方案。
AutoTraveler 探索性遍历的实现,让我突然有了灵感,可以让一个驱动引擎把各个独立的 UI 测试、接口测试、数据层测试、稳定性测试、健壮性测试、前端性能测试、问题分析定位等串联起来,一次执行多种测试,形成测试闭环,测试结果以时间轴为纽带,建立上下文联系,这是个可以去探索实践的解决方案。
经过一段时间的折腾,已初步把 UI 测试、稳定性测试、前端性能测试串了起来,也做了一个 Report Center 提供公共 api,但这不足以形成前后端测试的一个闭环。通过多方调研,发现了 anyProxy 这个工具,可别小瞧它,有了它,再给它赋上一些测试的属性,就可以打通前后端的任督二脉了,下面是关于 Mock Better 的介绍。
总体目标
实现功能
Mock Better 是基于 anyproxy 做的,首先了解下 anyproxy 可以做什么,借用官网的图很直观。
anyproxy 通过代理实现中间人劫持的目的,说形象点就是前后端之间的交互被监听了,甚至可以控制了。既然拿到数据了,它提供的接口就可以实现篡改 Request、伪造 Response、篡改 Response 等功能。
1.编写 rule file 带技术费体力,更不要说管理和维护成本了。Mock Better 通过界面化、傻瓜化操作,创建和维护篡改规则,提升工作效率;
2.增加断言、测试集等测试属性,并通过一系列手段打通前后端测试的关联性,举几个例子发散下思维:
前端测试场景一:用常规手段造数据(后端传过来作用于前端)以达到边界值、等价划分类的场景代价很大,通过篡改接口返回的数据,就能很轻松的达到目的;
后端测试场景二:如果前端做了校验和屏蔽(前端传过去作用于后端),要校验后端服务或接口对边界值、等价划分类的的处理,通过篡改请求参数,也能达到目的;
后端测试场景三:如果通过断言发现报文异常,通过关键字调用后端的日志查询接口,定位错误上下文;
数据测试场景四:配置 DML 模板,以请求的参数作为查询条件获取预期结果,以响应的关键内容作为实际结果,进行数据层的校验(待实践探索阶段);
安全测试场景五:通过伪造请求头信息,篡改请求参数,校验后端的防守指数等。
以上,Mock Better 只是整个方案的一个重要组成部分,要真正达到目的,还需要驱动引擎(形式不限,如 AutoTraveler、UI Auto Test Framework 等)、测试结果处理引擎、统一报告平台等。
首先,传统的接口测试其重要性是不可替代的,我把它定义为属于前置阶段的,通过 Mock Client 实现的接口测试,特别是接口场景化测试可能需要解决模拟登陆态、自定义头信息、重定向控制、代理、加签、HTTPS、关联参数绑定、前端动态参数生成等问题。我相信包括我自己,很多人的接口测试框架已攻克了这些问题。
Mock Better 中的后端测试的区别在于以下几点:
1.不仅限于接口,还包括未渲染的页面;
2.相对于传统的接口测试,介入的时间节点更往后,和 UI 测试是并行的;
3.最大的区别在于它是探索性遍历、UI 自动化等引擎驱动的,所以模拟登陆态、关联参数绑定等不是它处理的,它是在端到端的交互过程中,通过配置的规则,动态命中完成测试的。说通俗点,传统接口测试要解决的问题,引擎都帮你做了;
4.相对于传统的接口测试,其寄生于真实环境之中,更贴近真实场景。
1. 获取证书
手机用户可直接扫描二维码,PC 用户下载后安装
2. 报文管理
点击 “开始录制”,后台会自动生成用于录制的规则文件,并启动 anyproxy 应用 rule file
var mysql = require('mysql');
var _host = 'localhost';
var _port = '3306';
var _user = 'root';
var _password = 'root';
var _database = 'qmock';
module.exports = {
replaceServerResDataAsync: function(req,res,serverResData,callback){
var col;
var connection = mysql.createConnection({
host : _host,
port : _port,
user : _user,
password : _password,
database : _database
});
connection.connect();
try {
if(null != serverResData ) {
serverResData = serverResData.toString();
var tempJson = serverResData;
tempJson = JSON.parse(tempJson);
col = null != tempJson ? tempJson.type : "";
} else {
col = "";
}
var insertSQL = 'insert into response(uuid,host,url,col,method,contentType,userAgent,email,tag,response) values(replace(uuid(), "-", ""),"' + req.headers.host + '","' + req.url + '","' + col + '","' + req.method + '","' + req.headers['content-type'] + '","' + req.headers['user-agent'] + '","quqing@com.cn","1",' + JSON.stringify(serverResData) + ')';
console.log("insertSQL -> " + insertSQL);
connection.query(insertSQL, function (err, res) {
if (err) console.log(err);
console.log("INSERT Return ==> ");
console.log(res);
});
} catch(err) {
console.log(err);
} finally {
callback(serverResData);
connection.end();
}
}
};
同一台服务器,录制功能启用后,其他人进入排队等候,完成录制后的报文列表,用户可以有针对性的创建篡改规则
3. 报文详情
显示报文的主特征信息,用户可以选择做篡改,也可以选择做断言
4. 创建篡改规则
这里是规则集的概念,一个规则集可以包含多个规则项,step by step 向导方式引导用户创建
启用规则,会根据配置自动生成用于篡改的规则文件,并启动 anyproxy 应用 rule file
module.exports = {
replaceServerResDataAsync: function(req,res,serverResData,callback){
try {
if(null != serverResData ) {
serverResData = serverResData.toString();
}
if( serverResData.indexOf("xxx") != -1 && req.headers.host.indexOf("xxx") != -1 && req.url.indexOf("xxx") != -1 && serverResData.indexOf("xxx") != -1 ) {
serverResData = serverResData.replace(/"xxx":".+?"/g, "\"xxx\": \"999\"");
console.log("update -> " + serverResData);
}
} catch(err) {
console.log(err);
} finally {
callback(serverResData);
}
}
};
5. 规则列表
用户可以修改、删除、启用篡改规则。为避免规则集相互之间的干扰和冲突,一次只能启用一个规则集
6. 修改规则
规则集包含多个篡改规则项
6. 更新规则项
为了适应接口可能发生的设计改变,提供了更小粒度的规则项修改,支持参数类型和篡改值的更新
向所有默默无闻的实践者致敬