Mock工具wiremock-py
作者 | 咪咪
wiremock-py
wiremock-py 是基于 WireMock 实现的, 使用 Python 批量生成不同测试场景下不同HTTP API 的 mock 数据, 然后作为 mock server 快速全面地对 API 进行测试。
背景
在数澜地产应用的前端测试中, 前端一般依赖于后端的数据, 前端通过后端在网关上发布的 HTTP API 获取数据. 要对前端进行充分的测试, 理想的做法是, 等待后端部署完成, 并且在数据层直接输入不同类型的数据源, 然后前端直接调用后端发布在网关上的 API 进行测试。
cdn.xitu.io/2019/7/18/16c03b5cfad2a16e?w=339&h=376&f=png&s=33802">
然而现实的情况是, 前端和后端的开发进度不完全一致, 如果前端先开发完成了, 必须要等后端对应的 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": {