本文为博主原创,未经许可严禁转载。
本文链接:https://blog.csdn.net/zyooooxie/article/details/113751095
之前写完 优化篇【一】 ,我想着好像也没太多东西 再优化了,然后又被打脸了,今天再来说一期。
接口自动化测试框架的 category,有兴趣,可以看看。
个人博客:https://blog.csdn.net/zyooooxie
使用不同登录账号
现在已写好的脚本中,登录账号是写死的,此账号为测试环境的白名单用户;
只是实际接口测试,涉及登录+权限,肯定要考虑不同账号的情况。
先看下 财务系统真实地设计:
整个系统因为涉及到不同模块(左面部分),要控制权限,故而 为不同用户 设计 不同角色(右面部分)。
我将其分类 如下:
A.无法登录的【无关账号】
B.可登录,但只有某些模块访问权限的【某些模块有权限,其他模块没有权限的普通账号】
C.可登录,所有模块都可访问的【Admin账号】
思路
实际使用时,我将其分类为:
A.【登录不成功】不登录 or 登录使用的错误账密
B.【登录成功】有权限、无权限
在excel中增加一列,保存某些想使用的账号 【不填写 默认使用白名单用户、0代表不登录】;
实际 执行当前用例时,先使用此账号登录 后跑此条用例。
代码
login_logout.py
class LoginLogout(object):
url_image = '/api/auth/get/verify/image'
url_key = '/api/auth/get/key'
url_login = '/api/auth/login'
url_logout = '/api/auth/logout'
def __init__(self, env=CommonFun.read_config(section='environment', option='value'), phone=12345678900, pwd='csdnzyooooxiecsdnzyooooxie'):
self.phone = phone
self.password = pwd
self.host = CommonFun.read_config(section=env, option='host')
self.port = CommonFun.read_config(section=env, option='port')
if self.port == '': # 生产环境
self.Host = ''.join(['http://', self.host])
else:
self.Host = ''.join(['http://', self.host, ':', self.port])
if env == 'test' or env == 'product':
self.url_image_new = ''.join([self.Host, self.url_image])
self.url_key_new = ''.join([self.Host, self.url_key])
self.url_login_new = ''.join([self.Host, self.url_login])
self.url_logout_new = ''.join([self.Host, self.url_logout])
else:
raise Exception('环境传参 有误')
def login(self):
session = requests.session()
res_key = session.get(self.url_key_new)
return_key = res_key.json()['data']
assert res_key.status_code == 200
if self.phone != 12345678900:
Log.info('请手输验证码,谢谢!!!')
res2 = session.get(url=self.url_image_new, params={'key': return_key})
assert res2.headers['Content-Type'] == 'image/jpeg'
code_str = input('image 验证码: ')
else:
code_str = '1234'
res_login = session.get(url=self.url_login_new, params={'code': code_str, 'key': return_key,
'password': self.password, 'phone': self.phone})
assert res_login.status_code == 200
assert res_login.json()['msg'] == 'success'
Log.info('登录成功')
return session, self.Host
多次使用某账号
若某账号,常常使用,总不能一直不停登录吧?
思路
使用时,先查询相关文件;
有,读取文件里的session;没有,登录后,保存session;
跑完所有用例,退出所有账号+删掉所有相关文件;
代码
@staticmethod
def change_account_session(default_session, account):
if account == '':
Log.debug('不填写 代表 默认账号')
return default_session
elif isinstance(account, float) and int(account) == 0:
Log.debug('0 代表 不登陆,直接请求')
return None
elif isinstance(eval(account), tuple) and len(eval(account)) == 2:
ph, pwd = eval(account)
Log.debug('当前账号+密码:{}、{}'.format(ph, pwd))
os.chdir(pickle_dir)
all_files = os.listdir(pickle_dir)
all_files_dict = dict.fromkeys(all_files, 'zyooooxie')
if all_files_dict.get('{}.pkl'.format(ph)):
Log.info('当前账号已经登录过,直接load')
f = open('{}.pkl'.format(ph), 'rb')
req_session = pickle.load(f)
else:
req_session, req_host = LoginLogout(phone=ph, pwd=pwd).login()
Log.info('当前账号第一次登录,准备dump')
f = open('{}.pkl'.format(ph), 'wb')
pickle.dump(req_session, f, -1)
f.close()
return req_session
else:
raise Exception('account:{} 传参有误'.format(account))
因为使用的是requests.session(),实际此条用例执行时,用例执行方法 run_case() 内都要改动:
# 发请求
change_account = TestRunner.change_account_session(session, account)
if change_account is None:
res = SendReq().req(test_url=new_url, request_type=method, test_data=new_req_data, header=header)
else:
new_session = change_account
res = SessionSendReq(session=new_session).req(test_url=new_url, request_type=method, test_data=new_req_data, header=header)
conftest.py
@pytest.fixture(scope='session')
def session_host():
warnings.simplefilter('ignore', ResourceWarning)
env = CommonFun.read_config(section='environment', option='value')
s = LoginLogout(env=env)
req_session, req_host = s.login()
yield req_session, req_host
s.logout(req_session)
filter_list = list(filter(lambda x: x.endswith('pkl'), os.listdir(pickle_dir)))
for f in filter_list:
with open(f, 'rb') as file:
session = pickle.load(file)
s.logout(session)
os.remove(f)
使用不同登录账号+多次使用 实际 run
不同账号
看下实际 权限:
预期结果:
- account值为0 是不登录,直接请求,肯定401
- account值为默认账号,白名单,成功拿到响应
- account值为16000000000是Test (没有权限),连续3个请求都是401
- account值为15000000000是balance(有权限),成功拿到响应
多次使用
用例选的是 account值为15000000000
断言结果
提取响应body 新方式
之前设计的是 提取响应body 使用 正则表达式,但我在操作过程中,发现有些问题。
b = """
{"status":"1","msg":"OPERATION SUCCEED","data":[{"id":228,"createTime":1612757834049,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777778093","phone":"17777778093","mail":"17777778093@123.com","roleIds":[]},{"id":229,"createTime":1612757834369,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777778604","phone":"17777778604","mail":"17777778604@123.com","roleIds":[]},{"id":230,"createTime":1612761674400,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777777982","phone":"17777777982","mail":"17777777982@123.com","roleIds":[]},{"id":231,"createTime":1612764630761,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777778650","phone":"17777778650","mail":"17777778650@123.com","roleIds":[]},{"id":232,"createTime":1612767472180,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777777883","phone":"17777777883","mail":"17777777883@123.com","roleIds":[]},{"id":233,"createTime":1612767960244,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777778202","phone":"17777778202","mail":"17777778202@123.com","roleIds":[]},{"id":234,"createTime":1612768536658,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777777787","phone":"17777777787","mail":"17777777787@123.com","roleIds":[]}]}
"""
# test = r'{"id":(\d{3}),"createTime":\d+,"updateTime":\w+,"deleted":\w+,"appUid":\w+,"userName":"17777777787'
test = r'"id":(\d{3}).*?"userName":"17777777787"'
print(re.search(test, b))
我的正则 是想使用 非贪婪模式,直接定位到最后一条【新建某记录后,一般都是最后一条】;
可是无法做到。查询的结果是 第一个id 到最后一条的userName。
从头就直接匹配上了,但想匹配 最后的“userName":"17777777787, 没辙,就得匹配到最后
当然可以换个正则表达式,
如下:
但我还是想 用python写方法 去提取body。
思路
一般我这儿接口返回的是 某个字典list【下图的data字段值】
2个想法:
- 直接取 list的最后一个元素,再使用dict的get() ;
- 直接获取整个字典list,根据某字段=某值,找到所需要的dict,再使用get();
代码
@staticmethod
def response_function_extractor(res: Response, extract, params_dict):
for e in extract:
Log.info(e)
if len(e) == 2:
# e[0] 含res_json
assert e[0].find('res_json') != -1
res_json = res.json()
result = eval(e[0])
params_dict.update({e[1]: result})
Log.info(params_dict)
else:
assert len(e) == 4
# e[0] 含res_dict
assert e[0].find('res_dict') != -1
res_dict = res.json()
dict_list = eval(e[0])
value = CommonFun.change_params_dictValue(e[2], params_dict)
for d in dict_list:
if d[e[1]] == value:
result = d[e[3]]
params_dict.update({e[3]: result})
Log.info(params_dict)
break
else:
raise Exception('提取失败:{}'.format(extract))
return params_dict
当然 run_case() 也要改;
我不太推荐 一次搞2种,我担心再出啥bug。
# 后置提取【最多一种】
if re_extractor != '':
# 正则
assert function_extractor == ''
res_text = res.text
re_extractor = eval(re_extractor)
params_actual_value_dict = CommonFun.response_re_extractor(res_text, re_extractor, params_actual_value_dict)
if function_extractor != '':
assert re_extractor == ''
# 方法
function_extractor = eval(function_extractor)
params_actual_value_dict = CommonFun.response_function_extractor(res, function_extractor, params_actual_value_dict)
提取响应body新方式 实际 run
这里再综合上述 不同账号+权限。
使用的是 财务系统的增加用户、查询用户list 、删除用户的接口【只有Admin账号 才有权限】;
使用正则表达式 提取
使用python方法 提取
整个过程
这次分享就主要这些内容;
我真的感觉没啥可再优化了。
希望别打脸。
交流技术 欢迎+QQ 153132336 zy
个人博客 https://blog.csdn.net/zyooooxie