1、新建py文件requests_,文件名称自定
2、封装分3个阶段,
封装控制台打印网络请求信息和返回信息
封装requests网络请求,已post请求为主
重新封装断言方法
improt json
from requests import Request
""" 1、封装控制台打印网络请求信息和返回信息 """
# 控制台带引网络请求信息
def print_req(preq):
print('=======================【请求信息】=======================')
print(f'【url】-{preq.method}:{preq.url}')
print(f'【headers】-{preq.headers}')
print(f'【data】-{preq.body}')
# 控制台打印网络返回结果
def print_resp(resp):
print('=======================【返回结果】=======================')
print(f'【status_code】-{resp.status_code}')
print(resp.json()) # 不美化打印返回数据
ppd(resp.json()) # 美化打印字典格式数据
print('========================================================')
# 美化打印字典格式数据
def ppd(dic):
print(json.dumps(dic,indent=2,ensure_ascii=False))
""" 2、封装requests网络请求 """
# 发送网络请求,返回原始response对象
def pure_request(s,url,data='{}',headers={'Content-type':'application/json'}):
req = Request('POST',url,data=data,headers=headers)
prep = req.prepare() # 准备url
print_req(prep) # 调用上面封装好的控制台打印方法,打印请求信息
# 发送网络请求,返回请求结果
resp = s.send(prep)
print_resp(resp) # 调用上面封装好的控制台打印方法,打印返回信息
return resp
# 发送网络请求,返回json对象
def request(s,url,data='{}',headers={'Content-type':'application/json'}):
result = pure_request(s,url,json.dumps(data),headers)
return result.json()
""" 3.重新封装json格式断言方法 """
"""
d1-预期结果,d2-实际返回结果
递归d2字典是不是和d1模板字典所期望值
d1中 键开头是 !,那么 d1 != d2
d1中 键开头是 * ,那么表示这个key不应该存在
d1中 键开头是 ?,可选字段,有的话进行验证,没有的略过
d1中 键开头是 @ ,那么检查长度是否正确
d1中 键开头是 [] ,那么检查只是否在一个范围内,例如[1,2,3,4]
d1中 值是None, 只检查是否有这个键
d1中 值不是None, 检查两个值是否一模一样
"""
def iterdict_dic(d1,d2):
# 判断数据类型是否一样
if type(d1) != type(d2):
return False,f"数据类型不同 {d1}={type(d1)},{d2}={type(d2)}"
# 判断字典内key和values是否期望值
for k,v in d1.items():
opposite = False # 取反操作符
if k[0] == '*': # 如果键的开头是*,那么表示键在实际值中不应该存在
k = k[1:] # 取键的实际值
if k in d2:
return False,f'多余字段:{k}'
elif k[0] == '@': # 如果键的开头是@,那么values的长度和实际值应相等
k = k[1:]
if k not in d2:
return False,f'缺少字段:{k}'
if len(v) != len(d2[k]):
return False,f'[{k}]的长度是:{len(v)},实际长度:{len(d2[k])}'
elif k[:2] == '[]': # 如果键的开头是[],那么实际值需要在预期值得范围内,比如实际值是1,预期值是[1,2,3]那么返回True
k = k[2:]
if k not in d2:
return False,f'缺少字段:{k}'
if d2[k] not in v:
return False,f"[{k}]的值是:{d2[k]},不在期望值内[{v}]"
elif k[0] == "?":
k = k[1:]
if k not in d2:
continue
elif k[0] == "!"
k = k[1:]
opposite = True
if k not in d2:
return False,f"缺少必要字段:[{k}]"
if v == None:
continue # 如果values等于None,跳过
if isinstance(v,dict):
res,msg = iterdict_(v,d2[k])
if ret == False:
return ret,msg
elif isinstance(v,list):
if not isinstance(d2[k],list):
return False,f"[{d2[k]}]是:{type(d2[k])},不是一个数组"
if len(v) > len(d2[k]):
return False,f"[{v}]长度和实际值:{d2[k]}长度不同"
for i in range(0,len(v)):
ret,msg = iterdict_(v[i],d2[k][i])
if ret == False:
return ret,msg
elif opposite:
if v == d2[k]:
return False,f"[{k}]的值不应该等于[{v}]"
else:
if v != d2[k]:
return False,f"[{k}]的期望值:[{v}] != 实际值:[{d2[k]}]"
return True,""
def iterdict_(d1,d2):
if type(d1) != type(d2):
return False,f"数据类型不同 {d1}={type(d1)},{d2}={type(d2)}"
# isinstance-主要用来判断变量是否属于某个内建类型,对象的类型与参数二的类型相同则返回True,否则返回False
if isinstance(d1,dict): # 如果预期值是字典格式则返回上面封装好的方法做判断
return iterdict_dic(d1,d2)
elif isinstance(d1,list): #如果预期值是列表格式则走下面方法
if len(d1) > len(d2):
return False,"列表长度不相等"
# 循环列表断言列表值
for i in range(0,len(d1)):
ret,msg = iterdict_(d1[i],d2[i]) # 判断预期值是否等于实际值
if ret == False:
return ret,msg
return True,""
else:
if d1 != d2:
return False,"预期值-[{d1}] != 实际值-[{d2}]" # 判断预期值是否等于实际值
else:
return True,""
# 最后调用断言方法
def iterdict(d1,d2):
ret,msg = iterdict_(d1,d2)
assert ret,msg
3、测试封装的requests_方法是否达到预期
1.新建文件test_iterdict
2.测试request网络请求方法,可用自家公司接口测试
【Terminal】控制台输入pytest -s test_iterdict.py即可运行以test开头或结尾的用例,可在控制台打印接口请求的详细信息
4、测试iterdict断言方法,(挑几个演示一下,有兴趣的可以自行调试)
预期值d1都出现在实际值d2中
d2中缺少字段
d1中值和d2实际值不相等
d1中值等于None则值判断键是否存在
键前面是?实际值中没有则跳过
键前面是?实际值中出现则验证
1、不方便用公司接口做演示,自己写了一个简易的小接口来编写一下测试用例,然后启动服务就可以本机调用测试接口
from flask import Flask,request
app = Flask(__name__)
"""
/api_test 接口路径
请求参数:user:liuage pwd:123456
正常请求-返回值:{'rc':1,'msg':'登录成功'}
异常请求-返回值:{'rc':999,'msg':'异常情况'}
"""
@app.route('/api_test',methods=['GET','POST'])
def login_api():
if request.method == "POST":
data = eval(request.data)
if 'user' not in data:
return {'rc':-999,'msg':'账号不能为空'}
if 'pwd' not in data:
return {'rc':-999,'msg':'密码不能为空'}
user = data['user']
pwd = data['pwd']
if user != 'liuage':
return {'rc':-999,'msg':'请输入正确账号'}
elif pwd != 123456:
return {'rc':-999,'msg':'请输入正确密码'}
else:
return {'rc': 1, 'msg': '登录成功'}
else:
return {'rc':-999}
if __name__ == '__main__':
# ip地址填自己电脑的IP地址,如:192.168.2.3
app.run('自己电脑IP地址',debug=True)
2、编写接口测试用例
from study.api_test import requests_
from requests import Session
s = Session()
class Test_login():
def setup_class(self):
self.url = "http://192.168.22.50:5000/api_test"
# 请求参数
self.payload = {
"user" : "liuage",
"pwd" : 123456
}
# 测试接口正常请求情况
def test_normal(self):
# 调用网络请求
result = requests_.request(s,self.url,self.payload)
# 填入预期值
fields = {
"rc" : 1,
"msg" : '登录成功'
}
# 断言返回值
requests_.iterdict(fields,result)
# 测试账号错误情况
def test_account_error(self):
payload = {
'user' : '111',
'pwd' : 123456
}
result = requests_.request(s,self.url,payload)
fields = {
'msg' : '请输入正确账号',
'rc' : -999
}
requests_.iterdict(fields,result)
# 测试密码错误情况
def test_pwd_error(self):
payload = {
'user':'liuage',
'pwd' : 12345
}
result = requests_.request(s,self.url,payload)
fields = {
'rc' : -999,
'msg' : '请输入正确密码'
}
requests_.iterdict(fields,result)
# 测试账号为空情况
def test_not_account(self):
payload = {'pwd':''}
result = requests_.request(s,self.url,payload)
fields = {'rc':-999,'msg':'账号不能为空'}
requests_.iterdict(fields,result)
3、【Terminal】终端输入pytest -s 用例文件名 运行用例
如果有一个用例没达到预期结果就会报错
感谢观看