moco框架在实际工作场景中的应用
why moco
在前后端分离项目,微服务项目上,不同服务要并行开发和测试,必然绕不开mock。在对方未提供服务的情况下,使用mock来模拟对方的返回。
Moco就是这样一个开源的工具,支持HTTP协议,socket协议,请求格式支持form格式,json格,xml格式,还能使用正则和函数进行灵活的定义,一个jar就可以运行,是一款轻量级的实用工具。
环境准备
安装JDK8,过程不再赘述,java -version显示是jdk8即可。
moco下载
运行moco
1. 配置json
运行moco前需要先创建一个json文件,定义moco将会模拟什么样的接口,我们先建一个简单的config.json文件测试一下get请求:
[ {
"description":"模拟一个get请求",
"request":{
"uri":"/getInterface",
"method":"get"
},
"response":{
"headers":{
"Content-Type":"text/html;charset=gbk"
},
"text":"这是一个mock测试例子"
}
}
]
注:因为返回串有中文,所以在response中加了个编码格式设置
2. 运行moco
moco的运行非常简单,直接跑jar文件就可以,moco的运行命令为:java -jar moco-runner-<version>-standalone.jar http -p 12306 -c foo.json
注:http指定启动http服务,也可以使用start启动所有协议的服务
-p指定端口
-c指定配置文件路径
例如:java -jar moco-runner-1.1.0-standalone.jar start -p 18888 -c config.json
运行后程序显示监听了18888端口,说明moco已经起来了:
在linux上启动也是一样:
也可以使用nohup命令后台启动,如:nohup java -jar moco-runner-1.1.0-standalone.jar start -p 18888 -c config.json >moco.log &
注:这里用moco.log做了个日志的重定向
3. 访问moco提供的mock接口
上面配置的这个get请求比较简单,直接在浏览器里面访问即可:
这样moco就可以作为mock服务使用了!
典型应用:配套文件嵌套
在实际测试中,往往要测试很多接口,做很多mock工作,可以通过moco的文件嵌套来灵活的分模块分服务组织配置文件,从而进行有效的管理。
测试人员各自负责配置自己模块的mock,最后组装在一起统一提供mock服务,当然,如果谁的配置文件写错了,撤掉就是,毕竟moco支持动态加载,爽啊!
举个例子,同时有两个模块的接口需要做mock,比如模块A里的interface1,B模块里面的interface2要mock,我们分别建两个目录A和B,在A和B下分别配置interface1.json, interface2.json,目录结构如:
A
|——interface1.json
B
|——interface2.json
下面我们先把各个接口的json配置写好,我这里举的例子都是比较常见的接口类型。
1. 写一个带参数的get请求
interface1.json:
[{
"description":" interface1接口mock,get请求,请求参数A,B,返回success,正常返回200",
"request":{
"uri":"/interface1",
"method":"get",
"queries":{
"A":"paraA",
"B":"123"
}
},
"response":{
"headers":{
"Content-Type":"text/html;charset=gbk"
},
"text":"success",
"status":200
}
}]
2. 写一个json格式的post请求
Interface2.json:
[{
"description":"interface2接口mock,post请求,json格式,请求参数C,D,返回json,参数E,F,正常返回200",
"request":{
"uri":"/interface2",
"method":"post",
"json":{
"C":"paraC",
"D":12306
}
},
"response":{
"json":{
"E":"paraE",
"F":"paraF"
},
"status":200
}
}
]
3. 配置文件组装与服务启动
将配置文件组装在一起,比如都放在mock-service.json文件里:
[
{"include": "A/interface1.json"},
{"include": "B/interface2.json"}
]
启动moco加载:java -jar moco-runner-1.1.0-standalone.jar http -p 18888 -g mock-service.json
tips:使用嵌套方式加载配置文件,文件如果有语法错误,moco只报告加载的哪个json文件有问题,具体的错误只有非嵌套情况下才会打印,所以建议在嵌套之前先验证单个json文件的正确性.
4. 验证服务
我们先验证interface1:
这是个get请求,请求参数为form格式,把参数拼接子在接口url后,直接用浏览器请求即可:
再来验证interface2:
这是个post请求,请求参数为json格式,返回的也是json格式,可以使用工具如postman进行测试:
或者直接写python脚本发送请求:
import requests
url1 = 'http://localhost:18888/interface2'
json1 = {
"C": "paraC",
"D": 12306
}
运行结果如下:
灵活定制mock服务
要使用moco当然不会只模拟这么简单的接口,入参和出参也希望能动态配置,而不是写死的值。
所幸moco还是比较强大的,而且moco还支持动态加载配置文件,随时验证,不需要重启服务,相当友好。
下面就实际工作中我碰到的一些场景做些示例。
1. 请求参数为表单形式的post请求
我尝试过阿里的RAP2或直接用postman来做mock,发现这些工具对请求的参数是不做辨别的,比如同一个接口,我想测试不同入参情况下的接口逻辑,使用这些工具就只能写不同的接口名并定义返回值,非常死板,而且接口名和实际接口名不同,这就给后续的测试带来很多不必要的配置工作。但是moco对这一点支持的很好,接口uri可以重名,uri和入参匹配上,就能返回对应配置的返回值。
比如我们mock一个oauth的token获取服务,如果client_id传shenyq_test_client,我希望mock给我返回一个token值,如果client_id传test_clientxx,我希望mock给我一个错误的返回:
[{
"description":"oauth/token接口mock,post请求,表单参数",
"request":{
"uri":"/oauth/token",
"method":"post",
"forms":{
"scope":"read",
"grant_type":"client_credentials",
"client_id":"shenyq_test_client",
"client_secret": "RutgkkgTkmY8BXtQ=="
}
},
"response":{
"json":{
"access_token":"fc07aae2-be32-4feb-a4b1-6d602fde17f0",
"token_type":"bearer",
"expires_in":8784,
"scope":"read"
},
"status":200
}
},
{
"description":"oauth/token接口mock,post请求,表单参数,异常请求",
"request":{
"uri":"/oauth/token",
"method":"post",
"forms":{
"scope":"read",
"grant_type":"client_credentials",
"client_id":"test_clientxx",
"client_secret": "12RutgkkgTkmY8BXtQ=="
}
},
"response":{
"json":{
"error":"invalid_client",
"error_description":"Bad client credentials"
},
"status":400
}
}
]
用postman分别验证服务,正常请求:
异常请求验证:
2. 正则与template使用
match可以使用正则,语法参考:https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
Template可以使用函数,如:
"template": "${now("yyyy-MM-dd")}"
template还可以使用template获取请求中的参数,如:
"template": "${req.method}"
"template": "${req.version}"
"template": "${req.content}"
"template": "${req.headers['foo']}"
"template": "${req.cookies['foo']}"
"template": "${req.queries['foo']}"
"template": "${req.forms['foo']}"
下面搭建一个mock服务,用到moco的match和template功能:
[{
"description":"/match/test接口mock,get请求,通配name=*shenyqtest1,返回当前时间,正常请求",
"request":{
"uri":"/match/test",
"method":"get",
"queries":{
"name":{
"match":"\\w*shenyqtest1"
},
"age":"18"
}
},
"response": {
"text": {
"template": "${now(\"yyyy-MM-dd\")}"
},
"status":200
}
}]
验证:在url中调用这个接口,入参name以shenyqtest1结尾就能匹配
只是比较可惜,moco只支持string使用template和match,如果想要组织json参数带template,目前我采用了个笨办法,就是自己在text里面组装,如果使用方法有误,或者您有更好的方法请联系我:
[ {
"description":"/match/test接口mock,get请求,通配name=*shenyqtest34,返回当前时间,正常请求",
"request":{
"uri":"/match/test",
"method":"get",
"queries":{
"name":{
"match":"\\w*shenyqtest34"
},
"age":"18"
}
},
"response": {
"text": {
"template": "{\"time\":${now(\"yyyy-MM-dd\")}}"
},
"status":200
}
}]
验证一下:
或者也可以配置成post请求:
[{
"description":"/match/test接口mock,post请求,通配name=*shenyqtest34,返回当前时间,正常请求",
"request":{
"uri":"/match/test",
"method":"post",
"forms":{
"name":{
"match":"\\w*shenyqtest34"
},
"age":"18"
}
},
"response": {
"text":{
"template": "{\"time\":${now(\"yyyy-MM-dd\")}}"
},
"status":200
}
}]
用postman验证:
- 设置cookie和httponly
模拟一个get接口,返回时设置cookie,设置httponly属性,并根据请求参数返回登录者名字,这里使用template功能,配置文件如下:
[{
"description":"/cookie/test接口mock,get请求,通配name=*345,返回当前时间,正常请求",
"request":{
"uri":"/cookie/test",
"method":"get",
"queries":{
"name":{
"match":"\\w*shenyqtest345"
},
"age":"18"
}
},
"response": {
"cookies":{
"loginName":{
"value": "shenyqtest",
"secure":"true"
}
},
"text": {
"template": "${req.queries['name']}"
},
"status":200
}
}
]
验证请求,通过浏览器F12可以调出开发者模式,查看返回的cookie值和属性: