在测试阶段或者在一些特殊环境,经常因为某些数据导致客户端崩溃.
比如后端接口数据返回某个字段为空、某个字段类型变了、数组为空等.此时如果客户端没有兼容这些异常行为,大多数情况会导致崩溃.
所以为了保证客户端在任何异常数据的情况下都能兼容 (不崩溃),需要进行异常数据测试.
在以前手工测试阶段,我们一般借助 fidder 或者 charles 工具.对返回结果拦截修改数据,再进行数据的 maplocal.
比如对一个字段的返回值改为空,如下图:
如 3s、5s、10s
如 404、500、503
制造异常数据的关键是对数据拦截并修改,上述讲到的使用 charles 工具.但是 charles 工具不太灵活、不能开发扩展脚本.
调研了一些目前比较好用的代理工具:
本文主要选用 mitmproxy 二次开发,主要是因为结合 python 语言和 python 类库,比较容易上手.
在 mac 环境下安装命令:
brew install mitmproxy
mitmweb 是提供的 web 可视化工具,界面提供查看和筛选接口数据.
启动命令:
mitmweb
mitmdump 无交互界面的命令,可与 python 脚本对接.
启动命令:
mitmdump
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
拦截请求的request
"""
from mitmproxy import http
def request(flow: http.HTTPFlow):
# redirect to different host
if flow.request.pretty_host == "example.com":
flow.request.host = "mitmproxy.org"
# answer from proxy
elif flow.request.path.endswith("/brew"):
flow.response = http.HTTPResponse.make(
418, b"I'm a teapot",
)
flow.request.headers #获取所有头信息,包含Host、User-Agent、Content-type等字段
flow.request.url #完整的请求地址,包含域名及请求参数,但是不包含放在body里面的请求参数
flow.request.pretty_url #同flow.request.url目前没看出什么差别
flow.request.host #域名
flow.request.method #请求方式。POST、GET等
flow.request.scheme #什么请求 ,如https
flow.request.path # 请求的路径,url除域名之外的内容
flow.request.get_text() #请求中body内容,有一些http会把请求参数放在body里面,那么可通过此方法获取,返回字典类型
flow.request.query #返回MultiDictView类型的数据,url直接带的键值参数
flow.request.get_content()#bytes,结果如flow.request.get_text()
flow.request.raw_content #bytes,结果如flow.request.get_content()
flow.request.urlencoded_form #MultiDictView,content-type:application/x-www-form-urlencoded时的请求参数,不包含url直接带的键值参数
flow.request.multipart_form #MultiDictView,content-type:multipart/form-data
时的请求参数,不包含url直接带的键值参数
flow.response.status_code #状态码
flow.response.text#返回内容,已解码
flow.response.content #返回内容,二进制
flow.response.setText()#修改返回内容,不需要转码
在上面提到可以拦截 request 和 response,那么就可以对 response 数据做修改,再返回修改后的数据.
基于上面提到修改数据规则,随机多拦截数据做随机修改.
保存修改前和修改后的数据,方便数据 diff.
设计流程图如下:
例如 png、html、img 这些静态资源不需要修改,直接返回即可.
目前做到的规则是,随机对接口做修改,基于定义好的随机事件.
修改字符串是修改数据中最常用的方式,比如对返回 json 中的某一个字段或者多个名字一样的字段做修改 value 值.
因为返回的 json 一般都是深层嵌套 json 数据,所以需要把 json 中的每一字段都组装成 tree,形成一条链路.
在 json 中有东西叫 jsonpath,通过 jsonpath 就可以找到对应的 value 值.
那么我们就可以遍历 json,生成 N 条 jsonpath 路径.
有个上面我们提到 jsonpath,我们就可以随机对一条或者多条 jsonpath 修改数据,然后重新 set 回去.
但是和遗憾的是 python 并没有现成的根据 jsonpath 修改 json 的库,
在网上参考了一些帖子并重新修改了一些代码,代码片段如下:
在修改数据之后调用 flow.response.set_text 把修改后的数据 set 回去.
把每次数据修改前和修改后保存到本地 log 文件.
报告中记录每次的接口数据信息.
报告详情中展示接口详情.
最终的执行命令:
mitmdump -s proxyserver.py(二次开发脚本名字)
手机代理设置端口 (127.0.0.1:80)
在使用这个脚本是结合了 monkey 脚本,也发现了一些因为数据异常的崩溃.
在着手开发工具的初衷在于降低移动端的 crash 率,因为在发现崩溃 top5 内基本上会看到空指针这类错误,但是这类错误也是在服务端异常或者某些特定场景下才会导致某个字段为空或者某类数据为空.
现在客户端处理的流程是,基于业务逻辑加入判空处理.但是这也是见一个问题处理一个问题,并且代码中加入冗余的异常处理逻辑.
前几天读了"美团外卖 AndroidCrash 治理之路"的帖子,这篇帖子中谈到了对于"crash"预防应该大于治理,提到很多种预防手段.所以我基于这种思想,看看是否能在测试阶段通过自动化手段 mock 出一些数据,来验证客户端的稳定性和兼容性.
但是如何要想把异常数据测试落地在工作上,其实最难的是如何"说服"开发同学解决这类问题.
代码地址: https://github.com/xinxi1990/mitmfuzz
美团外卖 Android Crash 治理之路
https://tech.meituan.com/2018/06/14/waimai-android-crash.html
在 anyproxy 上做 mock 和 fuzz 测试
https://www.testwo.com/article/999
https://github.com/zhangzhao4444/XMonkey
代理服务器 AnyProxy
https://www.jianshu.com/p/2074f7572694
https://github.com/fwon/electron-anyproxy
深入了解 mitmproxy
https://www.cnblogs.com/c-x-a/p/9753526.html
Python 实现 json 数据的 jsonPath(精简版) 定位及增删改操作
https://www.cnblogs.com/shouke/p/10157458.html
App 爬虫神器 mitmproxy 和 mitmdump 的使用
https://cloud.tencent.com/developer/article/1151810