开源测试工具 App 接口测试工具之 apimock (动态 mock 服务器返回)

zhangzhao_lenovo · October 31, 2016 · Last by 天琴圣域 replied at March 30, 2017 · 7174 hits

app接口mock server端返回一工具,一剑在手,跟我走! app崩溃就靠它了

优势

  1. 动态实时mock
  2. 命令配置灵活,键值支持精确及正则模糊检索,提供多个fuzz功能
  3. 容易理解,方便易用

适用场景

app对服务端返回的容错测试

服务端返回一旦出错后果很严重,app轻则ui异常重则崩溃,像api 404,502,长时间无返回,延迟, 返回空body,键值错,无值,异常值这几类一般必测

项目中使用该工具后效率有了明显提升


原理

  • 如何实时mock?

app通过pc上fiddler代理访问api

通过运行proxy xxx 给mock server 注入mock 配置,支持重置

借助fiddlerscript实现包转发给mock server,server根据运行时动态配置对包进行劫持修改,修改后再返还给fiddler

fiddler.js 重写OnBeforeResponse将指定包劫持转发给mockserver

static function OnBeforeResponse(oSession: Session) {
if (isautocap && oSession.HostnameIs(filterUrl) && oSession.responseCode == 200) {
oSession.utilDecodeResponse()
var rawbody = System.Text.Encoding.UTF8.GetString(oSession.responseBodyBytes);
var j=Fiddler.WebFormats.JSON.JsonDecode(rawbody)
if(typeof(j.JSONObject) == "object" && Object.prototype.toString.call(j.JSONObject).toLowerCase() == "[object hashtable]" && !j.JSONObject.length) {
try {
var api = oSession.PathAndQuery.split('?')[0]
rawbody = api + ':' + rawbody
var mockbody = mock('127.0.0.1', 8390, rawbody)
j=Fiddler.WebFormats.JSON.JsonDecode(mockbody)

mockserver.py 执行mock


def prep(self,data):
api, data = data.split(':', 1)
data = json.loads(data, encoding="utf8")
mock = {}
data['mock'] = mock
if api in self.api or not self.api:
try:
data = self.mock('', data)
  • 关键字索引及fuzz

索引找response中的关键字,并修改这个项的值

def key(data):
for k in self.key:
v= self.value[k]
if ':' in k:
str,k = k.split(':',1)
if str.lower() in ['regex','re','regexp']: data = regexpsearch(data,k,v) #关键字或正则模糊查找
else:
l_key= k.split('.')
data = exactsearch(data,l_key,v) #精确查找
return data

mockfun = {
'body': lambda: body(self.body),
'key': lambda: key(data),
'': lambda: data,
'clear':lambda : self.init()}
return mockfun[self.type]()

修改的值可以是具体值或者body,也可以是fuzz函数 也可自行实现其他fuzz

def setstrshorten(json, k,v):
v=str(v)
json[k] = (len(v) <= 3) and v or v[0:len(v) - random.randint(1, len(v)-1)]

def setstroverlen(json, k,v):
v=str(v)
n = 1025
# n=4294967294 max string ,..dot use!
for i in range(0, n): v += '1'
json[k] = v

def setstrillega(json, k,v):
json[k] = str(v) + ',*&#%()='[random.randint(1, 8) - 1] + 'H1 \u266a@\u5c0f\u8776\u6c42\u5b88\u62a4'

list = { 'del': lambda: delkey(json,k),
...
'none': lambda: set(json,k,None),
'0': lambda: set(json,k,0),
'-1': lambda: set(json,k,-1),
'maxlong': lambda: set(json, k, 9223372036854775808),
'*n': lambda: setintmultin(json, k,kv),
'cut': lambda: setstrshorten(json, k, kv),
'overlen': lambda: setstroverlen(json, k, kv),
'illega': lambda: setstrillega(json, k, kv),
...
}
return list[type]()
  • 控制台下发mock命令

python proxy.py -a /api/user/get -k errno=1 劫持api为/api/user/get,原response中errnor字段修改为1
python proxy.py -a /api/user/get -k re:name="H1 \u266a@\u5c0f\u8776\u6c42\u5b88\u62a4" 正则索引含name的修改为带乱码符号字符串
python proxy.py -r 1 清空mockserver中配置
python proxy.py -d 2g 模拟延迟2g网络
python proxy.py -b {} 返回空body
python proxy.py -b {xxx{'' 返回非json格式body
python proxy.py -a /api/user/get -k re:name=fun:del 模糊查找name关键字,将其键删除
python proxy.py -a /api/user/get -k data.uid=fun:maxint path查找 data节点下uid关键字 ,修改为溢出的int

是不是很容易理解


未来

考虑后续与思寒的appcrawler做插件对接,做自动遍历的mock测试


开源地址

https://github.com/zhangzhao4444/Apimock

如有bug和好想法可及时联系我

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 33 条回复 时间 点赞

好高产~

已经star

—— 来自TesterHome官方 安卓客户端

下次沙龙来分享下吧.

问下这类测试,在你们测试中优先度很高吗

楼主棒棒的,刚好后续可以关注下容错性测试,楼主360的童鞋吗

#4楼 @pacerron 很高吧 接口的业务逻辑正确后,就要尽量测试容错。服务器一旦崩溃挂起无响应至少要保证app无崩溃。以前出过一个案例,流量过大导致接口服务器崩溃了,app启动连弹幕长连接长时间无响应于是app崩溃,app上传崩溃日志后自重启,如此循环,服务器重启后又因不断重连再次崩溃。

#5楼 联想?

不错的落地方式,可以搞个mock平台,平台上直接hook response的,展示后端返回数据,用户可以可视化的修改response,再返回。 更屌啦

不太懂python勉强看了下git的代码,是否可以再向上封装一层,一个域名定制一些mock方式(不用每次手工去输入),封装成一些类。然后把这些定制持续集成起来,最终实现共产主义

#11楼 @jet 忘了吹捧了。 这玩意儿真屌,想象了一下在生产中按照自己的业务情况稍微扩展下代码,可极大提高效率。

#11楼 @jet 目标是做一个轻量化的工具,使用方便,简单即可。
现在是已经实现了一些mock ,但具体跑哪种mock 目前还需要人工定义。
最初考虑的以命令注入mockserver就是为了可以替代人工定义的来实现全自动,正在考虑如何做个能遍历api的mock测试,需要有个能不断触发api的入口

#10楼 @dengwei729 平台化太重了需要兼顾很多东西,其实mockserver就已经是个后端了

#13楼 @zhangzhao_lenovo 如果要简单,个人认为就用json文件对应一下特定api和mock就行了, 类似这种

[{
"apiName":"damnson",
"baseLink" :"xxx",
...
...
"mocks" : [mock1, mock2]
},
{
......
}
]

如果不增删改mock逻辑,核心代码不用动,就维护这个json就可以了。

#13楼 @zhangzhao_lenovo 另外fiddler里面劫持域名也可以写到配置里,这样方便不同域名的接口容错测试

#17楼 @jet 恩 fiddler里劫持域名原本是设计在server里的,但我测试发现fiddler每个包都会转发给server,这样server负担太重了 可能会有效率问题

#16楼 @jet 每个api一个json吗?那得维护很多个json
另外何时触发mock1 何时触发mock2呢?
之前我也考虑过这个问题

#18楼 @zhangzhao_lenovo 恩。不过如果你要和某种crawler结合,那必须避免所有的手工操作,否则意义就不大了。我觉得可以以一个接口的domain为单位执行一次完整的测试。比如domain A,下面的事情用脚本全部完成

配置文件的格式可以为(一个域一个json)

{
domain : "A",
apiList : [
{
"apiName" : "api1"
"mock 以及和app操作相关的等其他参数等"
},
{
"apiName" : "api2"
"mock 以及和app操作相关的等其他参数等"
},
...
]
}

#1 读取配置文件,domain为A
#2 fiddler.js 动态重写,将domain修改为A,自动启动一次fiddler,这样mockserver压力就不大了
#3 用一个入口文件,开始遍历本json的 apiList。 这个怎么做要看你和什么crawler结合,只要在json里面提供必要的参数就行
#4 做验证,结果监控输出等。最后要关闭所有资源 比如fiddler. 一次cycle就算完成
#5 可以用类jenkins简易做下持续集成(如果有必要的话)

核心的东西还是你那一套。。 不知道有没有道理。。

#19楼 @zhangzhao_lenovo 我觉得主要还是你想让他最终是个什么东西。 如果只是半手工这样提高一下效率,那目前这样再弄一下报告也就可以了,把一些常用的cmd或者sh命令维护一下,已经很不错了。 如果要结合crawler,那只要具体考虑到这个crawler是怎么遍历app的,何时触发mock1 mock2就全写在json里,自己约定一种规则就可以了。。 这些都是个人愚见。。

#21楼 @jet 恩 和crawler结合应该是可行的

#22楼 @zhangzhao_lenovo 由于我不熟悉python,我想用你这个思路换个语言山寨一套,后面的我就按我这边的需求去扩展,求恩准。

#23楼 @jet 没问题啊

楼主这种方式实现不错,但是存在一定的使用成本,依赖用户的环境
可以考虑用anyproxy做代理,利用前后端技术提供可视化编辑界面做一个Mock平台,动态绑定Rule及Mock数据可能会更加人性化。😀

#26楼 @fresh 平台就算了,本是个极简单的功能就应该更简单化。
anyproxy 之前有看过,实现动态的话也得另有个mockserver,修改rule_xx.js与mockserver通信来交换数据,实际本质上和用fidder是一样的。 最后考虑稳定还是选了fiddler

zhangzhao_lenovo [Topic was deleted] 中提及了此贴 16 Nov 15:02

很机智,不过我一般写东西,都是直接spring-boot 返回json ,眼界大开

#29楼 @zasdsd 这个原理还是相对简单,其实就是个中间件。 spring-boot不是很懂哈

#31楼 @zhangzhao_lenovo
今天使用了楼主的软件,有几点疑问,想当面和楼主交流下。不知道楼主能给一个qq否。
另外今天在使用软件的时候发现了一个小bug,可能楼主当初设置好了参数以后忘记改了。

根据上面我的截图,只要json的返回大小超过16384,那么就不能拿到完整的json,就算我们做好了monk的准备,程序也会报错。

建议可以把github上的代码修改下,把这个接受值调大一点。事实证明,16384的容量可能不够用。

PS:python2 和 python3 写法上真的有很多区别。不过在debug 这个bug的同时,学到了很多东西,很感谢。

#32楼 @lunamagic 多谢提醒,确实有此问题。之前为啥是16384已经记不清了。 qq77227005

dengwei729 回复

哈哈,咱还真有做。不过在想怎么写文档,因为配置起来稍麻烦。。。

抓包动态mock好有意思呀,fork看看,不过不懂Python还得现学,,,😔

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up