前端测试 基于 Anyproxy 的 Mock 服务器设计优化升级

terrychow · April 28, 2017 · Last by 文若 replied at December 10, 2019 · 4391 hits

一、前言

  • 最近陆陆续续地有testerhome中的朋友加我微信向我请教一些测试技术方案的实现,问得比较多的是mock服务器的实现,就是菜鸟搭建 Mock 服务器实践:Anyproxy+Moco,果然这是一个菜鸟搭建的东西,后面请教我的那些朋友都遇到了大坑,我先和所有看之前那套方案的朋友说声对不起,也应了我今年最喜欢说的那句话,去年啥都做,但啥都不精,好吧,不浪费篇幅,今天就特意应一些朋友的请求,也为了统一解答大家的一些疑点,我把后面我经过5个版本迭代的mock服务器分享给大家,希望对大家能起到那么一丢丢的帮助

二、之前的坑

  • 看过上面那篇文的朋友都能了解到,之前的mock服务器主要分为anyproxy和moco,后面发现了一系列的严重问题:

    1.moco中文编码处理很麻烦,而且moco起的作用是数据管理部分,那数据管理的话没必要用一个服务器那么重
    2.mock的api写死在代码里面,要改mock的api时需要改代码和重启服务器,而且其实每次只能mock一个api
    3.如果在测试过程中不需要mock时,就必须关掉服务器,那又要去设置一次代理,很麻烦
    4.api多了之后是否会有性能问题

好,那我的工作又来的,后面就通过5个版本的迭代,把上面的4个坑感觉还是很好的填了的

三、优化过程

  • 优先是对整个mock服务器的架构设计,直接了白

    1、api:管理需要mock的接口,可以直接用txt来按行管理

    2、data:用于存放要mock的数据,可以和api一一对应,以json文件格式来管理,数据内容是整个json

    3、conf:用于管理配置文件,目前最主重要的一个配置就是mock的开关

还有tmp是放在一些临时文件,好,上面的架构基本把接口和mock数据从mock服务器的代码中分离出来,那接下来就是主核心的mock服务器代码

具体的实现代码:

rule_mock.js
global.api = "testapi";   //mock接口标识
module.exports = {

summary:function(){
return "replace response data by local response now";
},

//mark if use local response
shouldUseLocalResponse : function(req,reqBody){
var fs=require('fs')
var msdata=fs.readFileSync('./conf/ms.json',"UTF-8"); //读取mock开关配置文件
var ms = JSON.parse(msdata);
var isOpen = ms['isOpen'];
if (isOpen==0) { //当isOpen为0时,把直接返回false跳过所有mock
console.log("isOpen is :" +isOpen+",skip mock!");
return false;
};
var file = fs.readFileSync('./api/mock_api.txt',"utf8"); //读取api文件
var apilist = [];
apilist=file.split(/\r?\n/ig); //按行分割存为列表
for (var i = 0; i < apilist.length; i++) { //循环mock,有多少个接口就mock多少次,支持多接口同时mock
var mockapi=apilist[i];
if(new RegExp(mockapi).test(req.url)){ //匹配是否为被mock接口
global.api=mockapi
return true;
}
};
return false;
},

dealLocalResponse : function(req,reqBody,callback){
var fs=require('fs');
console.log('mock api is: '+global.api);//使用global对象访问到"全局"变量
fs.readFile('./data/mock_data.json',function(err,data){ //读取mock的数据,data为一个json对象
if(err)
throw err;
var arr = JSON.parse(data);
try{
var respon=arr[global.api]; //取出json对象中对应api的数据
console.log("status :" + respon.status);
console.log("headers :" + JSON.stringify(respon.headers));
console.log("body :" + JSON.stringify(respon.body));
var newDataStr=JSON.stringify(respon.body);
callback(respon.status,respon.headers,newDataStr); //组合成一个全新的响应对象并返回
}

catch(e){
console.log(e);
console.log("you should go to set mock data for api: "+global.api); //作异常处理,当被mock的接口没有对应的mock数据时,提示并防止服务器退出
}
})

}
};

通过注释来看设计思路就很清晰了,代码里面最核心的两个参数global.api 和isOpen,isOpen用于控制是否要mock,global.api就是被mock接口和对应mock数据的一个关联,通过上面的方式,后面就不再需要通过moco来管理response,直接组合就能解决问题了,通过上面的设计思路,能够解决我上面所提到的坑的前3点,好,具体操作来看一下

四,操作演示

  • 通过anyproxy来启动服务器执行mock脚本和在浏览器设置代理的方法上面文章已经有了,这里就不再演示了,直接验收mock过程 1、测试通过mock发送消息和对讨论设为关注的接口:cim.msg%3Areply和setFlag(接口为已缩写)

以及测试的数据:

{,
"cim.msg%3Areply":
{"status" : 202,"headers" :{"content-type" : " text/x-json;charset=UTF-8"},"body": {"code":"FA_OVERFLOW","var":{}}},
"setFlag":
{"status" : 202,"headers" :{"content-type" : " text/x-json;charset=UTF-8"},"body": {"code":"FA_FORBIDDEN","var":{}}}
}
  • 目前的mock开始isOpen设置为0,执行mock ----------
  • 可以从上面看到,由于mock开关已关闭,所有请求都会跳过 ----------
  • 当isOpen不为0时,开启mock ----------
  • 可以看到mock已生效 ----------
  • 同时可以连续mock多个接口,还可以随意增减改需要mock的接口,无需重启服务器 ----------
  • 如未对mock的api设置对应的mock数据 ----------
  • 就会有对应的提示,所以不用重启服务器,可以随时修改mock数据和api

所以大致的操作演示就是这样,其实还有第4点没说,当被mock的接口数到达一定数量时,mock是否会有性能问题,因为总是整个文件的读,文件量大了,肯定会有问题的,所以就可以通过具体的业务模块的接口来分布到不同的文件中管理,逻辑如下:

分割方法:

rule_mock.dev.js
global.apigroup="testapigroup";

//省略部分已展示过的代码,具体实现逻辑和结合自己项目本身来实现,这个实现方式是我们产品的,未必通用

try{
//console.log("req.url is :" +req.url);
var res=req.url.split('json?func=')[1];
var useapi=res.split('&sid=')[0];
//console.log("useapi is :" +useapi);
global.apigroup=useapi.split('%3A')[0];
console.log("apigroup is :" +global.apigroup);
}
catch(e){
console.log("api pass");
}

结合我们论客的接口设计特征,取出业务模块,比如global.apigroup=cim.msg,然后之后根据global.apigroup的值读取对应模块的文件,比如读取cim.msg.txt,同时加上异常处理

rule_mock.dev.js

//省略部分已展示过的代码
try{
var datapath='./data/'+global.apigroup+'.json';
var filetmp = fs.readFileSync(datapath,"utf8");
}
catch(e){
console.log('mock_data_file"'+global.apigroup+'" is not exist! skip to mock_data');
var datapath='./data/mock_data.json';
}

所有最后实现的效果,这样就能优化mock服务器的性能,可以看到明确的模块分组

同时对于未模块的mock接口,可以直接用回mock_api里面的数据,同时会跳过

就这样,我前面所说的4个坑就基本填完了

五,最后几句

  • 之前和大家说过的终于写出来了,技术方案要不断地改良,结合业务来实现,才能体现出技术方案的价值,同时也对个人能力的提高起很多的作用,我现在还继续在为我去年的一堆技术方案还债,同时开始着手学习如何制定测试策略,因为没有策略的测试感觉就是瞎测,去年一年没把东西做好做精,很多是由于没有制定出测试策略来指导测试工作的开展,所以也欢迎大家继续共同讨论,共同学习,共同进步,我个人也是很喜欢开分享会的,毕竟分享会最大的受益人是演讲者,这种方式对自己的提升也很快,好吧,有空我可以分享一些作为一名90后工作不到2年的测试新人是如何学习测试技术和在职场中成长的一些经验,反正就是大家都多聊聊,多点分享,才会进步,谢谢啦
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 12 条回复 时间 点赞

再给大家开条路,anyproxy是一个平台,对比fiddler和charles会更加的通用,我们团队几个人也就只要启动一台anyproxy就够了,还可以在linux上玩,那意味着可以放到docker,用docker可以同时启动多个代理服务器来分别mock,而且管理也很方便,不妨试试

之前收藏了帖子,放假刚好找到学习下

—— 来自TesterHome官方 安卓客户端

terrychow #3 · May 01, 2017 作者
雪怪 回复

有位朋友和我说目前支持json格式的报文,想加入动态支持xml的,朋友您也可以考虑一些优化😁

"2.mock的api写死在代码里面,要改mock的api时需要改代码和重启服务器,而且其实每次只能mock一个api", 关于这一点,我看你前一篇帖子是提到修改了moco已经load的json, moco就会重新加载,这不就不用重启了么,而且api也是写在json里没有写在代码里啊,所以这一点不是很理解,可以再解释一下吗?

terrychow #5 · May 03, 2017 作者
yuanmin 回复

是选择的api 和选择mock数据写死在了rule_mock.js里面了,每次都只能拿到同一个mock数据,理解一下上一篇的逻辑应该就能理解了

terrychow 回复

嗯,前面看的不仔细,以为你说的这点是moco

anyproxy 使用 http层,我们这边 网络层通过jni实现的,是否有解决之道?

林家木子 回复

JNI这种本来anyproxy就不支持吧,如果需要做jni的mock,还不如直接在代码上对方法或api做,而且最好理解一下stub桩服务器和mock程序的区别吧,我也是最近才理解区分stub和mock

向阳 用 anyproxy 记录移动端 UI 测试数据 中提及了此贴 27 Sep 20:26

666 学习了

学习了,请问关于response的模拟,这种该如何实现:POST请求是json报文,其中有一个字段是ID,response需要获取这个ID作为响应json的一部分。

正想搞数据分离的mock,看了这个有些启发。谢谢

(node:36878) UnhandledPromiseRejectionWarning: TypeError: You may only yield a function, promise, generator, array, or object, but the following object was passed: "replace response data by local response now",您好,启动的时候会报这个警告,但可以启动服务成功,不过moco不到指定的json,啥问题呀,百度了半天没解决

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up