接口测试 我的 python 接口测试框架

测试小书童 · August 04, 2016 · Last by 测试小书童 replied at August 26, 2016 · 6168 hits

简单介绍

  • Win7 64,python 3,Pycharm. unittest
  • 读取配置文件--读取测试用例--执行测试用例--记录测试结果--生成html结果文件
  • 支持指定接口用例id的测试
  • 考虑到登陆后返回的token,useId给其他接口联合使用的情况
  • 使用html在线生成器生成xml,基于xml管理数据,只要改xml文件,不用改其他代码。
  • 检查点的定义
    • 检查第一层的全字段
    • 如果有嵌套层,检查第一层的全字段后,检查嵌套层的model的type类型

模块类的设计说明

  • Httpbase.py 读取http的域名和端口
  • Config.py http方法的封装,可以支持多协议扩展,get,post
  • Runner_m.py 核心代码。run_case是程序的入口
  • Htmlreport.py 结果生成html文件

xml模型

<root>
<title>导购码接口测试</title>
<host>dgm.XXXX</host>
<port>80</port>
<No>[]</No> # 指定需要运行哪些接口
<InterfaceList> # 第一个层固定预留,只用于登陆接口
<id>1001</id>
<name>登陆</name>
<method>POST</method>
<url>/Login</url>
<hope>{"appStatus":{"errorCode":0,"message":"操作成功"},"content":[{"user_sex":0,"fk_user_city":440300,"user_id":30,"nickname":"18576759587","user_phone":"18576759587","head_picture":"http:\/\/dgm.boweixin.com\/","has_finance":1,"user_state":1}]}</hope>
<params>{"account":"18576759587","password":"1112111","type":"0"}</params>
<login>user_id</login> # 登陆后返回的userid,token
<isList>0</isList> # 是否有嵌套
<!--Version=3.0&UserName=18576759587&IMEI=868157020567821&Password=222222&City=%E6%B7%B1%E5%9C%B3%E5%B8%82&Province=%E5%B9%BF%E4%B8%9C%E7%9C%81&Plat=android&PhoneModel=H60-L02-->
</InterfaceList>
<InterfaceList>
<id>1002</id>
<name>厂家主页</name>
<method>GET</method>
<url>/GetFactoryHome?homeId=2</url>
<hope>{"appStatus":{"errorCode":0,"message":"操作成功"},"content":[{"business_name":"坤达点子","notice_img":"\/product\/20160718184134_321.jpg","user_type":1,"user_id":2,"goods":[{"good_price":45211.0,"good_id":12,"good_name":"艾欧","banner_picture1":"\/product\/20160719165135_8977.png"},{"good_price":199.0,"good_id":14,"good_name":"麒麟瓜1","banner_picture1":"\/product\/20160720102028_5352.jpg"},{"good_price":452.0,"good_id":6,"good_name":"实力产品","banner_picture1":"\/product\/20160718165448_2602.png"},{"good_price":99898.0,"good_id":11,"good_name":"越南芒果","banner_picture1":"\/product\/20160720100057_5877.jpg"}],"shop_img":"\/product\/20160718120144_3196.jpg","head_picture":"http:\/\/dgm.boweixin.com\/\/product\/20160718134528_4744.jpg","notice_id":1}]}</hope>
<params>{}</params>
<login>1</login> # 0不需要登陆后的参数,1表示需要登陆后的参数
<isList>1</isList> # 是否有嵌套层
</InterfaceList>

入口代码

gm = con_api_xml.ret_xml() # 读取xml
hb = con_api_xml.ret_http_base(gm) #读取http参数


#初始化报告
html_report1 = htmlreport.HtmlReport(gm)

# 测试用例()
class TestInterfaceCase(unittest.TestCase):
def __init__(self, testName, hope, index):
super(TestInterfaceCase, self).__init__(testName)
self.hope = hope
self.index = index
def setUp(self):
self.config_http = config.ConfigHttp(hb.host, hb.port)
def function(self):
response = ""
if self.index == 1: # 登陆的接口测试
if gm[self.index]["method"] == "POST":
response = self.config_http.post(go.URL, go.PARAMS)
go.REALLY_RESULT = eval(response)
print(go.REALLY_RESULT)
hope = eval(self.hope)
# temp = testJson.compareJson(hope, go.REALLY_RESULT, gm[self.index]["isList"])
temp = check.compare(hope,go.REALLY_RESULT)
if temp:
go.LOGIN_KY = gm[1]["login"]
go.LOGIN_VALUE = go.REALLY_RESULT["content"][0][go.LOGIN_KY]
go.RESULT = 'Pass'
html_report1.success_num = html_report1.success_num + 1
else:
go.RESULT = 'Fail'
html_report1.error_num = html_report1.error_num + 1
else:
if gm[self.index]["login"] != "0":
go.PARAMS[go.LOGIN_KEY] = go.LOGIN_VALUE
if gm[self.index]["method"] == "POST":
response = self.config_http.post(go.URL, go.PARAMS)
if gm[self.index]["method"] == "GET":
response = self.config_http.get(go.URL, go.PARAMS)
print(type(response))
go.REALLY_RESULT = eval(str(response))
hope = eval(self.hope)
# temp = testJson.compareJson(hope, go.REALLY_RESULT, gm[self.index]["isList"])
temp = check.compare(hope,go.REALLY_RESULT, gm[self.index]["isList"])
print(temp)
if temp:
go.RESULT = 'Pass'
html_report1.success_num = html_report1.success_num + 1
# except AssertionError:
else:
go.RESULT = 'Fail'
html_report1.fail_num = html_report1.fail_num + 1
# 获取测试套件
def get_test_suite(index):
test_suite = unittest.TestSuite()
hope = gm[index]["hope"] # 预期值
# print(hope)
test_suite.addTest(TestInterfaceCase("function", hope,index))
return test_suite

# 运行测试用例函数
def run_case(runner):
html_report1.case_total = 0
case_list = hb.No
case_list = eval(case_list) # 把字符串类型的list转换为list
html_report1.case_list = case_list
temp_case = ""
if len(case_list) == False: #判断是否执行指定的用例ID
temp_case = gm
for index in range(1, len(temp_case)):
go.URL = gm[index]['url']
go.PARAMS = gm[index]["params"]
test_suite = get_test_suite(index)
runner.run(test_suite)
# 记录运行结果
gm[index]["result"] = go.RESULT
gm[index]["really_result"] = go.REALLY_RESULT
else:
for i in case_list:
for j in range(1, len(gm)):
if str(i) == gm[j]['id']:
go.URL = gm[j]['url']
go.PARAMS = gm[j]["params"]
test_suite = get_test_suite(j)
runner.run(test_suite)
gm[j]["result"] = go.RESULT
gm[j]["really_result"] = go.REALLY_RESULT
# 运行测试套件
if __name__ == '__main__':
start_time = time.time()
runner = unittest.TextTestRunner()
run_case(runner)
end_time = time.time()
html_report1.time_caculate(end_time - start_time) # 计算测试消耗时间
html_report1.generate_html( r'D:\\app\\auto_http34_test\\report\report.html') # 生成测试报告

检查点的代码

def compare(exJson,factJson,isList=0):
isFlag = True
if exJson.get("appStatus") == factJson.get("appStatus"):
if isList== False: # 如果没有嵌套层
return isFlag
data2 = exJson.get("content")
data3 = factJson.get("content")
for item2 in data2:
for item3 in data3:
keys2 = item2.keys()
keys3 = item3.keys()
if keys2 == keys3: # 如果嵌套层的key完全相等
for key in keys2:
value2 = item2.get(key)
value3 = item3.get(key)
if type(value3)==type(value2):
pass
else:
isFlag = False
break
else:
isFlag = False
break
else:
isFlag = False
print(isFlag)
return isFlag

运行结果用的是pyh拼接的html页面

  • 页面比较丑没有去美化

后续优化

  • 接口加密
  • 美化UI
  • 期望值从其他接口查询
  • 其他优化

github源码

后续

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

多谢分享,正想学下Python,很好的资料。

@lose 有没有兴趣来小红书看看么,我们也正在build一个基于Python的测试自动化

虽然py我不会,但是可以看得出是好东西。。👍

#2楼 @cesc 人在深圳,还想再沉淀一年,明年才会规划换工作的打算😬

#3楼 @jiazurongyu 对的,我所有的框架都是基于python来做的,包括自动化UI,monkey,远程的ssh监控服务器等等,学了python后,实在对其他语言不感兴趣,哈哈

好吧,原来是把requests又封装了一遍

#6楼 @jphtmt

看代码,封装的比较简单

import requests
# 配置类
class ConfigHttp:
def __init__(self, host, port):
self.host = host
self.port = port
self.headers = {'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; rv:29.0) Gecko/20100101 Firefox/29.0'}
# 设置http
def set_header(self, headers):
self.headers = headers
# 封装HTTP GET请求方法
def get(self, url, params):
url = "http://"+self.host+":"+self.port+url
try:
r = requests.get(url, params=params, headers=self.headers)
r.encoding = 'UTF-8'
return r.text
except Exception:
print('no json data returned')
return {}
# 封装HTTP POST请求方法,支持上传图片
def post(self, url, data=None, files=None):
data = eval(data)
url = 'http://' + self.host + ':' + str(self.port)+url
r =requests.post(url, files=files, data=data)
print(data)
json_response = r.text
return json_response

更着楼主学!zzz

#7楼 @lose 嵌套层的话就是list的比较了吧

def dictlistcompare(self, list1, list2):
if len(list1)!=len(list2):
return 0
for i in list1:
if i in list2:
continue
return 0
return 1

#4楼 @lose 好的,期待你分享更多干货

数据库校验?数据恢复?

#3楼 @jiazurongyu 你不睡觉的啊团子哥!

赞,有些问题想请教能加个QQ吗

慎用eval, 如果你希望将json字符串转成dict,可以导入json模块

#14楼 @jacexh 哈哈,对的宁愿用ast.literal_eval,嗯,用json dumps,loads去优化,只是偷了下懒,都被你发现了😁

#12楼 @neyo 暂时不考虑去数据库操作

赞,研究研究

#15楼 @lose 你用xml管理测试数据,很多数据的话会不会很麻烦?

#18楼 @mads 不会,最简单是复制粘贴而已,当然也可以我的生成器,自动生成xml

#19楼 @lose 你的生成器咋用


没有嵌套层直接返回值了 不是没有做任何比对吗

#21楼 @klxiaoqi 看代码的上一行,就是对比的第一层无嵌套的情况,有嵌套才会对比嵌套层

TESTERHOME上的测试框架都是这样的吗。。写一个接口用例 就你这个框架你看要增加 修改几段代码? 效率太低了,XML要增加,而且你的XML完全没有考虑 参数包括HEAD的参数化,数据库依赖, 包括接口依赖,场景初始化,如果一个接口的基本功能都验一遍 比如每个参数是否为空,特殊字符,长度等等 你XML就需要多少段。。再加上 业务的用例 ,检查点的增加,太难维护了。。
而且你这个检查点也无法验证一个JSON串的结构,包括重名数据 等等 。

#23楼 @kofalex

TESTERHOME上的测试框架都是这样的吗。。写一个接口用例 就你这个框架你看要增加 修改几段代码? 效率太低了

  • .怎么感觉你在抨击什么?大家只是分享下自己的想法和思路,还有接口测试在每个人眼中都不一样

太难维护

  • 就现在而言,我的这个接口测试框架,不用改任何代码,直接配置xml即可,至于你说的难维护没有发现哪里难维护,直接复制,粘贴而已,如果不想复制,粘贴,请用接口生成器

比如每个参数是否为空,特殊字符,长度等等 你XML就需要多少段。。再加上 业务的用例 ,检查点的增加

  • 嗯,按照你的意思还有其他情况也要考虑,服务器的cpu,io,注入,men,mysql语句运行情况,tomcat,java资源,cdn,分布式情况的考虑

关于后面优化

  • 接口测试的检查点,我不会去查数据库
  • 帖子后面我说了优化情况,会考虑到多接口联合测试
  • 会考虑一个接口测试两次,正常测试和异常测试。正常测试就维持我现在不变的情况,异常测试还没有想好怎么模板化,如果你有什好建议,麻烦提出来,谢谢

以楼主为目标学习!楼主学习python多久了?

你的框架应该是使用的author = 'shouke' 写的框架,希望你注明作者,别把别人的写的框架说成自己的,要尊重作者,尊重知识产权。

#26楼 @loading shouke是我老朋友,对之前确实是看了他的那个框架,包括报告生成报告。但是你请仔细看看我和他框架的区别。他的框架所有的缺点在我这里完全没有。。已经把他的新浪微博贴上去了

测试小书童 关闭了讨论 12 Aug 17:23
测试小书童 重新开启了讨论 12 Aug 17:24

#2楼 @cesc 你是小红书的哇

#30楼 @mdl_xiaotuanzi 是啊,你是在上海的么

#27楼 @lose 淡定淡定,别理喷子

#27楼 @lose 楼主你好,可以留个QQ吗勾搭一下

测试小书童 [Topic was deleted] 中提及了此贴 26 Aug 10:59
测试小书童 关闭了讨论 26 Aug 12:28
测试小书童 python 接口测试框架测试报告 (二) 中提及了此贴 05 Dec 08:55
测试小书童 关闭了讨论 22 Jun 14:24
测试小书童 Appium 开源分享优化版 中提及了此贴 20 Mar 17:53
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up