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

优势

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

适用场景

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

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

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


原理

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)

索引找 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]()

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 和好想法可及时联系我


↙↙↙阅读原文可查看相关链接,并与作者交流