接口测试 Http 接口测试框架 (开源 + 已投入实际项目中)

Heyniu · August 08, 2016 · Last by yangqinyuan replied at August 04, 2017 · 9054 hits
本帖已被设为精华帖!

相关链接

说明

由于部分内容涉及公司机密,已用字母替换,不影响阅读

实际效果

  • 验证1000个接口平均耗时6s(看机器配置及网速)
  • 第一次投入使用,马上发现5个接口异常并且该验证过程不到30s的时间

框架的下一步

  • 目前已兼容我们公司所有app

  • response body全字段验证(含字段类型)

    • 已有思路
  • 字段变化导致2个大问题

    • 由原本int(0/1)变成了布尔型
    • 时间戳长度由10位突然变成13位
  • 尽量避免日常监控中跑接口对外网数据/用户的影响

    • 目前做法是屏蔽相关接口
    • 目前状态
    • 跑接口时创建的数据id有变化,然而删除接口还是调用老的id去删除,导致数据删除不到
    • 改进
    • 拦截创建数据的response body取出对于id
    • 拦截删除接口request body,传入上一步拦截的id

最新框架图(红色部分未完成)

Http接口测试框架

部分代码

配置文件

http接口测试框架配置信息

tester = tester
project = A
versionName = 2.2.2
versionCode = 237
host = a-webapi.test.b.com
getTokenHost = http://a-webapi.test.b.com/api/System/GetToken
loginHost = http://a-webapi.test.b.com/api/User/LoginV2
loginInfo = Phone=13750199962&Password=FGgIwe5oCdk%3D
SessionsPath = D:\Fiddler Sessions
ApiURL = http://apihelper.b.com/Home/API/c
# SpecialSessions >> 屏蔽的接口
SpecialSessions = ['GetToken', 'LoginV2', 'LogOut', 'BookingV2']

启动测试入口

"""
运行api测试总入口
"""


import sessions.Request


def launcher_api_test():
"""
1.获取接口列表
2.与本地sessions对比
3.差异化文件,是否继续
3.1否 继续录制接口
3.2是 开始跑接口
:return:
"""

r = sessions.Request.Request(0) # 0 >> A 1 >> B
r.start()


if __name__ == "__main__":
launcher_api_test()

头部、登录接口

def __get_token_header(self):
"""
生成token头部
:return:
"""

des = self.__get_token_des()
arr = (des, self.conf['systemType'], self.conf['Model'], self.conf['Release'], self.conf['DeviceId'],
self.conf['versionCode'], self.conf['versionName'], self.conf['AppBuild'], self.conf['DeviceOS'], "0")
authorization = self.AUTHORIZATION_TOKEN % arr
headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'Authorization': authorization}
response = self.session.post(self.conf['getTokenHost'], headers=headers)
if json.loads(response.text)['StatsCode'] == 200:
data1 = json.loads(response.text)['Data']
self.time = data1['Time']
self.TOKEN_NAME = data1['TokenName']
self.TOKEN_VALUE = data1['TokenValue']
else:
print("GetToken失败,请手动检查")
utils.HandleJson.HandleJson.print_json(response.text)

def __login_session(self):
"""
调用登录接口,这样后面的接口都能正常访问了
:return:
"""

url_login = self.conf['loginHost']
headers = self.__get_session_header(url_login.split('api/')[-1])
data_login = r'%s' % self.conf['loginInfo']
response = self.session.post(url_login, headers=headers, data=data_login)
if json.loads(response.text)['StatsCode'] == 200:
data1 = json.loads(response.text)['Data']
self.uId = data1[self.head_uid]
self.uName = data1['NickName']
self.uPhone = data1['Phone']
self.SessionId = data1['Sid']
self.uType = data1['UserType']
self.uuid = data1['UID']
else:
print("登录失败,请手动检查")
utils.HandleJson.HandleJson.print_json(response.text)

接口片段

Request url: a-webapi.test.b.com/api/Circle/AddCancelCollectCircle
Request header: POST /api/Circle/AddCancelCollectCircle HTTP/1.1
Host: a-webapi.test.b.com
Accept: text/json
Authorization: Digest t="2016-08-04 16:41:19",SystemType="2",u="Circle/AddCancelCollectCircle",r="59e93eb1a6625adc6bff5ede5945a2f7",DeviceId="ffffffff-8416-49fe-3fdc-6ee400000000",Model="SM-N9100",DeviceOS="22",Release="5.1.1",VersionName="2.2.2",VersionCode="239",PushToken="",uId="3353",uName="123456ejz",uPhone="13750199962",SessionId="%2FZhckUf9%2Fd2soQZhYofjN021SdWUpLv0aW%2F3CJBr71vtOl1YHJda6J8p6P1hsQS0P3kqirm%2BtPs%3D",uType="1",bDChannelId="",bDUserId="",AppBuild="239",uUID="2255"
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 21
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/3.0.1

Request body: CircleId=6418&IsAdd=1
Response code: 200
Response body: {"StatsCode":200,"Message":"收藏成功","Data":null,"Other":null}
Session end



Request url: a-webapi.test.b.com/api/Circle/AddCancelCollectCircle
Request header: POST /api/Circle/AddCancelCollectCircle HTTP/1.1
Host: a-webapi.test.b.com
Accept: text/json
Authorization: Digest t="2016-08-04 16:41:22",SystemType="2",u="Circle/AddCancelCollectCircle",r="26dcb55ee9ac79995c21656517c455e8",DeviceId="ffffffff-8416-49fe-3fdc-6ee400000000",Model="SM-N9100",DeviceOS="22",Release="5.1.1",VersionName="2.2.2",VersionCode="239",PushToken="",uId="3353",uName="123456ejz",uPhone="13750199962",SessionId="%2FZhckUf9%2Fd2soQZhYofjN021SdWUpLv0aW%2F3CJBr71vtOl1YHJda6J8p6P1hsQS0P3kqirm%2BtPs%3D",uType="1",bDChannelId="",bDUserId="",AppBuild="239",uUID="2255"
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 21
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/3.0.1

Request body: CircleId=6418&IsAdd=0
Response code: 200
Response body: {"StatsCode":200,"Message":"取消收藏成功","Data":null,"Other":null}
Session end

接口列表

接口列表

你需要做的

  • 环境配置

    • Python 3.x
    • fiddler一枚(配置抓取手机请求)
    • PyCharm
  • token/session替换

    • 替换成你们项目对应的token等
    • 修改配置文件
    • 修改response body json 判断逻辑
  • 替换fiddler js

    • 项目根目录的fiddler js整个文件内容替换fiddler的js
    • 打开fiddler的Customize Rules功能
    • 删除所有内容,并把fiddler js内容全部拷贝进去
    • 修改拦截的host等信息
    • fiddler保存请求

fiddler js 自定义信息


//自定义参数设置
public static var filterUrl = "a-webapi.test.b.com";
public static var filePath = "D:\\Fiddler Sessions\\Api\\";
public static var filePathForRequested = "D:\\Fiddler Sessions\\Requested.txt";
public static var filePathForErrorResponse = "D:\\Fiddler Sessions\\ErrorResponse.txt";
public static var filePathForVerifyRequset = "D:\\Fiddler Sessions\\VerifyRequset.txt";
public static var filePathForRemoveSession = "D:\\Fiddler Sessions\\RemoveSession.txt";
public static var filePathForAddSession = "D:\\Fiddler Sessions\\AddSession.txt";
  • github拉取代码
  • 欢迎一起交流

GitHub

框架地址

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

够大,先mark

Heyniu #3 · August 08, 2016 作者

#1楼 @sycing 谢谢支持😂

Heyniu #4 · August 08, 2016 作者

#2楼 @darker50 谢谢支持,欢迎提供建设性意见😂

好东西,先收藏了在说!!!

Heyniu #6 · August 08, 2016 作者

#5楼 @qg__gq 谢谢支持😂

碉炸天!GetApi 少了文件扩展名?

Heyniu #8 · August 08, 2016 作者

#7楼 @link1220 舞草 这都被发现了,不过未影响使用,你加上吧,我更新一下

思寒_seveniruby 将本帖设为了精华贴 08 Aug 15:57

加精理由: 提出了一整套相对完整并符合持续集成的接口测试框架. 并无私的开源. 部分设计非常有亮点. 符合我们TesterHome的技术风格. 期待楼主以后逐步放出系列的教程科普.

Heyniu #11 · August 08, 2016 作者

#10楼 @seveniruby 谢谢支持,希望TesterHome越来越好👍

感谢分享,真心很强大啊

Heyniu #13 · August 08, 2016 作者

#12楼 @monkey 共勉,目前只是简单快速的反应接口问题,下一步会更精准 针对性一些,不用每个接口都写用例

作者来点非http接口的,譬如hessian,soap等其他协议的 O(∩_∩)O~

Heyniu #15 · August 09, 2016 作者

#14楼 @kasi 还没接触过,不懂😂

额~~想确认下这个框架干的就是将python cilent和fiddler抓取的内容进行diff?

Heyniu #17 · August 09, 2016 作者

#16楼 @taflo
你看文章中的最新框架图吧,它做图里的事情

TXT里的结构是什么样的?

Heyniu #19 · August 09, 2016 作者

#18楼 @kofalex 你看下本文章的接口片段就知道了,包含以下信息
request url
request header
request body
response code
response body

这个TXT是自己写的 还是录制的? 怎么录制?

Heyniu #21 · August 09, 2016 作者

#20楼 @kofalex 是录制的,你可以看我的github拉取源码(在文章末尾,喜欢的话请star哦)
然后看一下https://testerhome.com/topics/5481 fiddler保存会话
然后有兴趣可以看一下接口测试框架的一系列文章,本文章开头有贴地址

加个QQ 聊聊吧 我也在弄接口正好 179461035

的确高大上~只是考虑了正常情况,没有考虑到接口缺参数,参数的类型错误问题?

Heyniu #24 · August 09, 2016 作者

#23楼 @lose 这个初衷是接口回放,就是上线前回归接口的,你说的这个功能还没做哦,后面考虑加上

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

Heyniu #25 · August 09, 2016 作者

#22楼 @kofalex 明天我加你,现在下班了😂

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

#17楼 @heyniu 😅 哎~~~算了白问了

我想问下,1000个接口是一个个的请求后,然后通过fiddler保存下来的么?

Heyniu #29 · August 10, 2016 作者

#27楼 @jaychang1989 是的,就是你测试的时候 接口就自动保存下来了,也可以monkey的时候跑

#29楼 @heyniu 比如接口返回的数据多了一条,这个时候你脚本怎么处理的?

Heyniu #31 · August 10, 2016 作者

#30楼 @jaychang1989 如果接口是错误的,可以右键这个请求,移除该接口

#31楼 @heyniu 我没说清楚,是同一个接口,比如用户列表接口,现在新增了一个用户之后,返回的数据多了一条,我看你上面写的好像是与本地存的数据进行对比,这种情况,应该是会出现结果不匹配,这种情况你怎么处理?

Heyniu #33 · August 10, 2016 作者

#32楼 @jaychang1989 fiddler只是录制接口哦,如果录制的接口是错误的,那么保存的也是错误的(需要移除),字段校验是在接口回归的时候验证(跑接口的时候),现在这个功能还在开发中,目前只是校验接口异常,好比移动端的崩溃

可以拿来试试怎么用么

Heyniu #35 · August 10, 2016 作者

#34楼 @mads 可以,操作方法看文章中的,你需要做的

#35楼 @heyniu ok,感觉高大上

Heyniu #37 · August 10, 2016 作者

#36楼 @mads 欢迎一起交流,目前还是比较简单的,类似monkey发现崩溃问题一样,只能发现严重问题,后续加上重试机制、更详细的字段校验、考虑从接口回放做成接口自动化

有没有2.7版本的

Heyniu #39 · August 11, 2016 作者

#38楼 @cloudwind 没有哦,你看着转换一下格式就好了

Heyniu [Topic was deleted] 中提及了此贴 15 Aug 09:48

为什么要从页面上那接口,不懂啊

Heyniu #42 · August 17, 2016 作者

#41楼 @NsirNixp 你能描述清楚点吗,没太懂你意思

感谢楼主的无私奉献,对Python不熟,有时间拜读下代码.

Heyniu #44 · August 17, 2016 作者

#43楼 @luaxlou 加油

#42楼 @heyniu 获取api的目录,我看了一下,你是从页面是拿接口的,
,但是页面上怎么会拿到接口url呢

Heyniu #46 · August 17, 2016 作者

#45楼 @NsirNixp 这里不是有一个正则匹配吗,你可以学习了python爬虫知识,回过头看一下这个你就明白了

#46楼 @heyniu 不是,我是说前端的接口信息应该是配置在js的ajax里调用,静态也上你匹配html标签里的信息,怎么会有接口信息呢

Heyniu #48 · August 17, 2016 作者

#47楼 @NsirNixp 这是一个接口文档的url,里面包含全部url

Heyniu #50 · August 17, 2016 作者

#49楼 @NsirNixp 其实你不必纠结这些东西,你只需要这是去拿接口列表的就好

#50楼 @heyniu 嗯,晓得了,谢谢

Heyniu [Topic was deleted] 中提及了此贴 18 Aug 09:18

感谢分享啊,周末好好参考一下

请问这个框架可以做业务场景多接口测试么,对于业务场景多接口的测试有什么思路没?

来了TesterHome之后,发现每位分享者的接口思路都挺独特的,点赞;

Heyniu #56 · August 21, 2016 作者

#54楼 @defias 目前还没有单独的开展接口业务测试,目前框架已经具备这样的能力,需要单独组装接口

虽然有些看不懂,还是感谢分享

这个分享很赞,是否可以联系一下做深入交流吗?

Heyniu #60 · August 31, 2016 作者

#59楼 @kendydrm 企鹅335827476

Heyniu #62 · August 31, 2016 作者
Heyniu #64 · August 31, 2016 作者

#63楼 @mads 汉子😅

#64楼 @heyniu 珍珍是你女朋友吧

只支持post?

Heyniu #67 · August 31, 2016 作者

#66楼 @mads put get这些方法都一样的,目前我们项目的就是post,你自己拓展吧

你们没有防重放攻击的机制?

{
"code": 31,
"msg": "请勿进行重放攻击"
}

签名认证?像这种
http://developer.dianping.com/app/documentation/signature

碰到这两种东东,感觉有些刺手。

#68楼 @qi_ling2005 这个肯定是有做的,框架里面的这些信息涉及机密,被我屏蔽了,望谅解

#69楼 @heyniu 恩哈,如果项目签名、加密方式一变,相关的这块代码就得变了

#70楼 @qi_ling2005 变的只是内部,对外公开的api还是不变的哦

只能先点个赞了。。。

很厉害 学习一下。

Heyniu [Topic was deleted] 中提及了此贴 14 Sep 15:36

我的理解,简单的说功能就是比对两个环境的接口差异吧?包括参数和返回字段的对比。然后做基本的数据验证。lz是这样的么?
设计目的也是为了防止意外引入的大问题吧。

#75楼 @cyj86
目前验证包括:
response code
json里面的服务器特有的code
全字段校验(字段缺少,新增,改变)
全字段类型验证
时间戳的长短验证
后续可以支持自定义的字段验证

先点个赞,我发现现在用python的越来越多了

#77楼 @linaipeng 优势挺多啊

楼主看到你的文章,清理数据这块有2个问题
1、如果清理接口本身就失败
2、部分场景只存在创建,没有删除接口
你是如何处理的?

Heyniu #80 · October 09, 2016 作者

#79楼 @harmo
1.清理接口你指的是清理创建数据的接口吗,如果不能确保是否成功就需要通过数据库验证了
2.创建数据对应线上日常监控,如果没有删除接口,创建的数据可能就影响线上了,一是考虑排除,不创建;二是通过操作数据库删除这个创建的数据

楼主想问下你做这个框架是为了接口的回归测试吗?

Heyniu #82 · October 17, 2016 作者

#81楼 @justcby 目前是,之后会加上接口业务测试。压力测试那些

Heyniu [Topic was deleted] 中提及了此贴 01 Nov 09:55

能介绍下 最后一句话 整个过程贯穿 jenkins 执行吗?具体的步骤是怎样?(公司要开始搞接口测试,想多了解一下)🙏 🙏

结合公司具体接口想了一下,如果要确保接口有数据返回,进行全字段验证请问请问是怎么实现的?参数控制是怎么做的?

Heyniu #87 · November 25, 2016 作者

#86楼 @liuhao121 就是拿到本次接口返回的所有字段(遍历json)与上次接口返回的字段作对比,查看有什么变化(新增/删除/修改字段)
jenkins 只需一条命令就可以启动了,前提项目当模块安装在python上

这个可以


替换fiddler的JS后,报这个错,请问是哪里有问题吗?

90Floor has been deleted

#91楼 @heyniu 我这边没有这个目录哦,还有请问下 不能自动保存各接口txt需要注意哪里的配置呢?谢谢

Heyniu 接口业务测试 -- Java 版 中提及了此贴 01 Jun 11:52

楼主的设计思想是,读取fiddler录制下来的接口,跟本地数据或者跟上一次录制下来的接口的字段信息进行对比,从而检验接口的正确性,是吗?那为啥不直接跟开发要一份接口文档呢,这样的话就可以直接对接口做自动化测试了,然后再集成到Jenkins上做日常监控~我看你写的代码有些复杂,花了半天时间去看了下代码,代码基本是做处理录制下来的接口信息,读写文件,操作文件夹,处理json字段等,有些方法的思路还是不错的,但是我个人觉得用这样的方式去测试接口回归接口不算是一个高效的方式~

不知道自己有没有理解错误,也请多多指点~

Ningxw 关于接口自动化的那些事 - 基于 Python 中提及了此贴 12 Jul 14:45

感谢楼主!

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