看过上面那篇文的朋友都能了解到,之前的 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 服务器代码
具体的实现代码:
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 点,好,具体操作来看一下
以及测试的数据:
{,
"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":{}}}
}
所以大致的操作演示就是这样,其实还有第 4 点没说,当被 mock 的接口数到达一定数量时,mock 是否会有性能问题,因为总是整个文件的读,文件量大了,肯定会有问题的,所以就可以通过具体的业务模块的接口来分布到不同的文件中管理,逻辑如下:
分割方法:
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,同时加上异常处理
//省略部分已展示过的代码
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 个坑就基本填完了