开源测试工具 接口测试工具之 httpapi (类似 gor,抓流量,回放,diff,fuzz)

zhangzhao_lenovo · 2016年10月31日 · 最后由 minizuo 回复于 2018年04月26日 · 4462 次阅读
本帖已被设为精华帖!

接口测试一工具,好不好用,用了就知道

优势

  1. 无需写测试用例!
  2. 所做即所得,真实记录流量,多次测试复用!
  3. 结构化 diff,简化结果校验!
  4. 中间化插件,解决动态参数!
  5. 自动 fuzz 测试各种容错!
  6. 无缝对接持续集成

原理

  • 如何实现了记录和回放?

二次开发 fiddlerscript 实现自己的 OnBeforeResponse,开启 fiddler 实时记录抓取指定数据包的请求和回应

static function OnBeforeResponse(oSession: Session) {
        if (isautocap && oSession.HostnameIs(filterUrl)) {
            if (oSession.responseCode != 200 || oSession.GetResponseBodyAsString().IndexOf("\"errno\":0") != 1){
                record(oSession,filePath + "refuse.gor",8);

static function record(oSession:Session,fpath:String,op:int){
        oSession.utilDecodeResponse();
        var sBuilder = new StringBuilder();
        sBuilder.Append("\n"+"Request id: 1 " + getmd5(oSession.url) +" "+ oSession.Timers.ClientBeginRequest.Ticks+" "+getmd5(oSession.PathAndQuery.split('?')[0])+"\n");
        sBuilder.Append("Request protocol: " + oSession.isHTTPS+"\n");
        sBuilder.Append("Request url: " + oSession.url+"\n");
        sBuilder.Append("Request api: " + oSession.PathAndQuery.split('?')[0]+"\n");

从记录的文件中读取数据包原始的请求结构,经中间件处理后,调 requests 库发包

def send(url,method,payload,headers,**attrs):
    if method == 'POST':
        r = requests.post(url, data=payload, headers=headers,**attrs)
    if method == 'GET':
        r = requests.get(url, data=payload, headers=headers,**attrs)
    return (r.status_code,r.json(),r.headers)
  • 如何解决动态 token?

运行时获取包的 response 再经中间件 middleware 修正后续包的 request

def fifoprocess(queue,n):
    while True:
        try:
            n-=1
            j=n
            str = queue.popleft()
            (code,body, header)=process(str)
            while(j>0):
                queue.append(middleware.rule(str,body,header,queue.popleft()))
                j-=1
            fifoprocess(queue,n)

利用一个双向队列弹栈发包,修正后再入栈。该方法效率有待改善

middleware 具体算法需根据项目情况自己实现逻辑

def rule(str,body,header,pstr):
    """
    url=str.split('Request ')[3].split("?")[0].split(" ")[1].replace("\n","")
    if url=='www.3663.com/api/msg/send':
        body=eval(body)
        token=urllib.parse.quote(body["data"])
        pstr,_=re.subn("content=(.*)&","content="+token+"&",pstr)
    """
    return pstr
  • 如何 diff?

首次运行测试时会根据包返回与记录文件中原包返回做结构化对比,生成差异 white.txt,认为是一次 diff baseline

white.txt
www.xxxx.com/api/index/banner header.Transfer-Encoding
www.xxxx.com/api/room/get header.Content-Length
www.xxxx.com/api/room/init body.data.room_info.stream_status
www.xxxx.com/api/room/init body.data.anchor_info.uid
www.xxxx.com/api/room/init body.data.chat_info.url
www.xxxx.com/api/room/get header.Date

后续测试会根据本次包返回与上次包返回做对比,差异屏蔽掉白名单之后的 diff 作为测试结果供人工校验

也可手动删除 white.txt,连续 2 次测试来生成 baseline

实际测试人员只需确认记录的原包,生成的 white.txt,以及后需的 diff。无需用例无需手工重复执行,简化了其测试成本

  • 关于 fuzz

同样利用记录文件,读取原包 request 结构自动遍历一些基本的 fuzz 测试 ,如缺参数,空值,无值,字符串裁减、扩展、异常值,超长,特殊值等。

也可自行添加 fuzzcase 如 xss 等


适用场景

  1. fuzz 测试 -- 新接口
  2. 回放测试 -- 接口线上监控,回归测试,环境切换

开源地址

https://github.com/zhangzhao4444/httpapi

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

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

好东西,支持一个

mac 好像不行吧

#2 楼 @pacerron 恩,我在 win7-64 上实现的,没有 mac 可用。其实也可以借助 mac 上的抓包工具 比如 burpsuite,方法是类似的,猜测也有类似 OnBeforeResponse 接口重写下,把包记录 record 文件 后面都一样了

嗯,我有个问题,就是回放的时候,比如有些 key 只能生效一次,回放再调用一次就不能用了。
另外,你这个和论坛里也有个人做的复制接口好像差不多

#4 楼 @pacerron 有些 key 只能生效一次,回放再调用一次就不能用了。 说的是动态参数比如 token 吧,这个利用中间件可以动态解决的

谢谢分享,最近搞好想研究下这方面的知识

思寒_seveniruby 将本帖设为了精华贴 10月31日 20:56

加精理由: 开源 设计思路优秀

#5 楼 @zhangzhao_lenovo 没明白 能细说下吗

很使用哦 点赞

#11 楼 @heyniu 是的,你的

#12 楼 @pacerron 懒了,很久没更新这套了,服务器一直没空帮我自定义测试报告,停在那里了

#13 楼 @heyniu 呵呵,空下来再搞搞

#9 楼 @pacerron 这个工具灵感来源于那个开源的 gor,一直跟了一段时间。

解决 token 等动态参数需要自编写 middleware.py 的逻辑,大致流程:
获取 packet a 的 request-->send a-->得到真实的 a response-->经过 middleware 中间件-->根据逻辑修正 other packet-->send b

def rule(str,body,header,pstr): #str a,a 的 response body,a 的 response header,pstr b 的原始包
url=str.split('Request ')[3].split("?")[0].split(" ")[1].replace("\n","")
if url=='xxx/api/msg/send':

body=eval(body)
token=urllib.parse.quote(body["token"])
pstr,_=re.subn("content=(.*)&","content="+token+"&",pstr)
return pstr
判断如果 a 是指定 url ,获取其动态返回的 token,按正则替换到 b 中

仅是个例子,实际通常需要一些加密算法,或者从 cookies 中提取

我也用 fiddler 脚本实现类似用例录制的功能,查了好久才发现时.net 脚本😅

想请教一下 fiddlerjsconf.ini 里的

"filterUrl":"www.3663.com",

用来做什么?

#17 楼 @orangec 劫持的指定域名,相当于录制的一个过滤条件,根据自身项目情况修改

zhangzhao_lenovo [该话题已被删除] 中提及了此贴 11月16日 15:44

mark 后续需要时,再来讨取

python 碰到 java 写的那些自定义的加密方法就比较难处理了吧~其中最常见的莫过于签名了

34楼 已删除
23楼 已删除
35楼 已删除

#21 楼 @kasi 哦。感觉还好,我接触的几个项目 加密这块 python 都可以搞定,其实实在不行 python 也能直接调 jar

回复出 bug 了。。点 1 次出了 4 条

#26 楼 @zhangzhao_lenovo 重复的帮你删了:)

@zhangzhao_lenovo python 是可以调用 jar 包,但是一次工程中,只能调用一次,而不腻频繁调用,这个是 jpype 的一个坑,所以不合适,加密是自定义的,除非用 python 自己重写加密算法,不然只能走其他方式处理的

#28 楼 @kasi 我们的接口 mock 也需要做一些数据处理,用的是 java,比如你提到的加密,因为是安卓项目,所以直接用 java 比较稳妥一些。

@simple 是的,python 处理这一块还是比较吃力,虽然有方法解决,但是碰到例如 hessian 类协议的处理便捷性不如 java,我这边的接口语言后续重构的时候会切换为 java

感觉动态结果验证是最重要也最不好做的,目前还处于只验证状态码、格式、和相对固定的字段的阶段

有个疑问,如果只是给的接口文档怎么测试?

嗯可以把它想象成一个录像回放机 利用对比的思路进行测试。有无接口文档影响不大,录制时的操作决定了要测哪些内容

你好 我正在研究这个 请问 怎么可以联系你 私聊啊 大神:😀

我在抓取一个 APP 的新闻动态评论,然后通过 filter 过滤 选择关键词 comment,然后只剩下 我需要的会话。我想通过 fiddlerscript 编程能够自动保存我所筛选的信息到本地数据库或者文档之中。 还请指点🎁

瀚海星星 回复

sorry 刚看到 qq 77227005

最关键的问题:diff 背后的环境是如何搞的?如果是线上流量除了 token 替换还做了什么处理保证线下可以回放?如果是线下测试环境流量,如何保证能二次回放?所以想知道背后数据和依赖环境都怎么设计解决的呢,理想的 diff 肯定是既 diff 回包内容也 diff 了落地数据。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册