作者 | 咪咪
wiremock-py
wiremock-py 是基于WireMock实现的, 使用Python批量生成不同 测试场景 下不同HTTP API的 mock 数据, 然后作为mock server快速全面地对 API 进行测试。
背景
在数澜地产应用的前端测试中, 前端一般依赖于后端的数据, 前端通过后端在网关上发布的 HTTP API 获取数据. 要对前端进行充分的测试, 理想的做法是, 等待后端部署完成, 并且在数据层直接输入不同类型的数据源, 然后前端直接调用后端发布在网关上的 API 进行测试。
然而现实的情况是, 前端和后端的开发进度不完全一致, 如果前端先开发完成了, 必须要等后端对应的 API 开发完成后才能开始测试, 而且数据层的数据也不容易构造。
为了解决这个问题, 网关平台做了简单的 mock 功能, 每个 API 可以填写一个 mock数据, 然后前端调用 API 时直接使用这个 mock数据:
这种方式下, 网关充当了mock server:
但由于大家都使用同一个网关, 一个 API 只能保存一份 mock 数据, 所以有以下一些缺点:
不同的测试场景需要不同的 mock 数据来测试, 此时需要删掉上个测试场景的 mock 数据, 再创建新场景的 mock 数据才能进行测试
不能根据测试场景来按照一定的规则动态生成 API 对应的 mock 数据
不能多人同时使用测试同一个 API时, 只能都使用同一份 mock 数据, 不能各用各的
wiremock-py 可以解决上述这些问题: wiremock-py 通过传入不同的测试场景参数来生成不同的 mock 数据, 同时不同测试场景下使用的 mock 数据可以保存起来; 生成 mock 数据时, wiremock-py 支持使用Python和js代码来动态生成 mock 数据(也支持直接使用 json 数据, 如果 mock 数据中的数据量很大, 人工手写 mock 时的数据量会很大, 使用代码生成则比较容易); 不同的测试人员使用各自自己的 mock server, 不会影响到其他测试人员的测试。
测试人员需要做的是: 确定哪些 API 需要进行 mock 以及不同测试场景下对应的 mock 规则是什么。
依赖环境
Java 1.8.0_144
Node v8.6.0
Python 3.4.3
演示
快速开始
以贸数v1.1.0版本 测试环境为例演示使用 wiremock-py 对楼层客流分布和店铺客流分布两张图分布在3种场景下的测试方法
先确定本地浏览器能过正常访问 http://mall-data.com:9012
准备
克隆代码
git clone http://git.dtwave-inc.com:30000/baomi.wbm/wiremock-py.git
安装依赖
cd wiremock-py
pip install -r requirements.txt
npm install mockjs
生成目录
python mock.py -g "demo"
wiremock-py git:(master) python mock.py -g "demo"
DEBUG:root:mockdir=, scene=, target=, proxy_port=5506, generate=demo, wiremock=False, rewrite=False
DEBUG:root:正在生成目录 /Users/wangbaomi/autotest/wiremock-py/demo
DEBUG:root:创建目录成功: demo
DEBUG:root:创建目录成功: demo/js
DEBUG:root:创建目录成功: demo/json
DEBUG:root:创建目录成功: demo/python
DEBUG:root:创建目录成功: demo/wiremock
DEBUG:root:创建文件成功: demo/mappings.json
DEBUG:root:生成目录完成: /Users/wangbaomi/autotest/wiremock-py/demo
填写 mappings.json、json、python、js 数据
mappings.json 中填写内容:
[
{
"response": {
"default": {
"proxyBaseUrl": "target"
}
},
"mapping_name": "request url not start with /api",
"request": {
"method": "ANY",
"urlPattern": "/(?!api).*"
}
},
{
"mapping_name": "楼层客流分布",
"request": {
"urlPattern": "/api/v1/mall_data/customer_flow/every_floor\\?(.*)",
"method": "POST"
},
"response": {
"default": {
"proxyBaseUrl": "target"
},
"测试场景1": {
"bodyFileName": {
"json": "楼层客流分布.json"
}
},
"测试场景2": {
"bodyFileName": {
"python": "楼层客流分布.py",
"python_args": "测试场景2"
}
},
"测试场景3": {
"bodyFileName": {
"js": "楼层客流分布.js"
}
}
}
},
{
"mapping_name": "店铺客流分布",
"request": {
"urlPattern": "/api/v1/mall_data/customer_flow/every_shop\\?(.*)",
"method": "POST"
},
"response": {
"default": {
"proxyBaseUrl": "target"
},
"测试场景1": {
"bodyFileName": {
"js": "店铺客流分布.js"
}
},
"测试场景2": {
"bodyFileName": {
"json": "店铺客流分布.json"
}
},
"测试场景3": {
"bodyFileName": {
"python": "店铺客流分布.py",
"python_args": "测试场景3"
}
}
}
}
]
js 文件夹中新建店铺客流分布.js文件, 内容为:
var r = {
"success": true,
"code": null,
"message": null,
"content": {
"meta": {},
"multi": {
"group": [
{
"id": "rank",
"name": "排名",
"value": [
1,
2,
3,
4
]
}
],
"result": [
{
"id": "the_shop",
"name": "店铺",
"value": [
"店铺1",
"店铺2",
"店铺3",
"第4个店铺"
]
},
{
"id": "customer_count",
"name": "人数",
"value": [
10,
100,
1000,
3242
]
}
]
},
"single": []
}
};
console.log(JSON.stringify(r));
js 文件夹中新建楼层客流分布.js文件, 内容为:
var r = {
"success": true,
"code": null,
"message": null,
"content": {"meta": {},
"multi": {
"group": [
{
"id": "the_floor",
"name": "楼层",
"value": [
"-1楼",
"1楼",
"2楼",
"3楼",
]
}
],
"result": [
{
"id": "customer_count",
"name": "人数",
"value": [
100,
1000,
5000,
567
]
}
]
},
"single": []
}
};
console.log(JSON.stringify(r));
json 文件夹中新建店铺客流分布.json, 内容为:
{
"success": true,
"code": null,
"message": null,
"content": {
"meta": {},
"multi": {