292 lines
14 KiB
Python
292 lines
14 KiB
Python
#!/usr/bin/python3
|
||
# -*- coding: UTF-8 -*-
|
||
# 设置utf-8 显示中文
|
||
"""
|
||
@Author: guo
|
||
@File:auto_assert.py
|
||
"""
|
||
import pytest
|
||
import re
|
||
import hamcrest
|
||
from hamcrest import *
|
||
from requests.models import Response
|
||
import jsonpath
|
||
from jsonpath import jsonpath
|
||
import sys
|
||
sys.path.append("../../")
|
||
|
||
from common.yml.get_yml_keys import GetYmlKeys
|
||
|
||
class AutoAssert:
|
||
"""进行自动断言"""
|
||
def __init__(self):
|
||
self.__ymlkeys = GetYmlKeys()
|
||
self.api_data_key = self.__ymlkeys.get_api_data_key()
|
||
self.assert_key = self.__ymlkeys.get_assert_key()
|
||
|
||
def auto_assert(self,testcase_ymldata:dict,response:Response)->None:
|
||
"""自动断言"""
|
||
ymldata = testcase_ymldata
|
||
res_tmp = response
|
||
|
||
print("\n----以下为【断言】内容:----")
|
||
# 先判断 response的类型是否为Response,当不为Response时.将当前的case置为fail
|
||
if type(res_tmp) != Response:
|
||
pytest.fail(f"传入的response的类型不为Response,即<class 'requests.models.Response'>,"
|
||
f"故将case置为fail")
|
||
|
||
api_data = ymldata[self.api_data_key]
|
||
assert_data = api_data[self.assert_key]
|
||
if assert_data == None:
|
||
print("该接口没有写断言")
|
||
|
||
elif isinstance(assert_data,list):
|
||
|
||
i = 1
|
||
for item in assert_data:
|
||
# expression = item[self.assert_key]
|
||
expression: str
|
||
expression = item
|
||
# 先判断表达式里,是否存在"res."这种字符串,如果存在,表示res为Response
|
||
if "res." in expression:
|
||
res = res_tmp
|
||
# 此时强制认为是使用 res.json(),不考虑 res.status_code
|
||
else:
|
||
res = res_tmp.json()
|
||
print(f"第【{i}】次断言,断言内容如下:")
|
||
print(f"此次执行断言的原始表达式为:{expression}")
|
||
""" 由于在不显示表达式里的具体内容,所以此处对表达式的内容进行了提取 """
|
||
hamcrest_str = "assert_that"
|
||
assume_str = "pytest.assume"
|
||
assert_str = "assert"
|
||
jsonpath_str = "jsonpath"
|
||
comma_str = ","
|
||
# exp_final = ""
|
||
# exp_reason = ""
|
||
|
||
# 判断是否为pytest.assume 断言
|
||
if expression.startswith(assume_str):
|
||
# 去掉字符串表达式中的 pytest.assume
|
||
# exp = expression.lstrip(assume_str)
|
||
re_exp = f"^({assume_str})"
|
||
exp = re.sub(re_exp,"",expression)
|
||
# 先判断是否按要求使用了jsonpath 提取器
|
||
if jsonpath_str in exp:
|
||
# 去掉最外层的左右括号
|
||
exp = self.__remove_bracket(exp)
|
||
# 进行字段的切割
|
||
exp_list = exp.split(jsonpath_str)
|
||
# 获取jsonpath提取器的相关内容
|
||
exp2 = jsonpath_str + exp_list[1]
|
||
# 再检测左右括号的数量是否相等,不相等则进行完善
|
||
exp2 = self.__check_bracker(exp2)
|
||
# 获取jsonpath提取器的数据
|
||
exp2 = eval(exp2)
|
||
exp1 = exp_list[0]
|
||
# 当为str时,要加上上引号,否则在执行表达式时,会提示NameError,未定义
|
||
# 在pytest.assume()常见的表达式里 没有逗号","的情况,所以可以直接拼接
|
||
if isinstance(exp2,str):
|
||
exp = f"{exp1}'{exp2}'"
|
||
else:
|
||
exp = f"{exp1}{exp2}"
|
||
# 再检测左右括号的数量是否相等,不相等则进行完善
|
||
exp = self.__check_bracker(exp)
|
||
# 目前只考虑下面几种判断符号,至于 is True,is False 之类的暂时不考虑.
|
||
character_list = ['==','!=','<','<=','>','>=','in','not in']
|
||
exp_final = f"pytest.assume({exp})"
|
||
exp_reason = f"断言失败,表达式为:{exp_final}"
|
||
print(f"使用jsonpath提取器提取数据后的表达式为(可能会出现缺少引号的情况):{exp_final}")
|
||
if '==' in exp:
|
||
exp_list = exp.split("==")
|
||
exp1 = exp_list[0]
|
||
exp2 = exp_list[1]
|
||
pytest.assume(eval(exp1)==eval(exp2),exp_reason)
|
||
|
||
elif '!=' in exp:
|
||
exp_list = exp.split("!=")
|
||
exp1 = exp_list[0]
|
||
exp2 = exp_list[1]
|
||
pytest.assume(eval(exp1) != eval(exp2), exp_reason)
|
||
|
||
elif '<' in exp:
|
||
exp_list = exp.split("<")
|
||
exp1 = exp_list[0]
|
||
exp2 = exp_list[1]
|
||
pytest.assume(eval(exp1) < eval(exp2), exp_reason)
|
||
|
||
elif '<=' in exp:
|
||
exp_list = exp.split("<=")
|
||
exp1 = exp_list[0]
|
||
exp2 = exp_list[1]
|
||
pytest.assume(exp1 <= exp2, exp_reason)
|
||
|
||
elif '>' in exp:
|
||
exp_list = exp.split(">")
|
||
exp1 = exp_list[0]
|
||
exp2 = exp_list[1]
|
||
pytest.assume(exp1 > exp2, exp_reason)
|
||
|
||
elif '>=' in exp:
|
||
exp_list = exp.split(">=")
|
||
exp1 = exp_list[0]
|
||
exp2 = exp_list[1]
|
||
pytest.assume(exp1 >= exp2, exp_reason)
|
||
|
||
elif 'not in' in exp:
|
||
exp_list = exp.split("not in")
|
||
exp1 = exp_list[0]
|
||
exp2 = exp_list[1]
|
||
pytest.assume(eval(exp1) not in eval(exp2), exp_reason)
|
||
|
||
elif 'in' in exp:
|
||
exp_list = exp.split("in")
|
||
exp1 = exp_list[0]
|
||
exp2 = exp_list[1]
|
||
pytest.assume(eval(exp1) in eval(exp2), exp_reason)
|
||
|
||
|
||
# 非常见的判断方式,直接使用eval
|
||
else:
|
||
|
||
exp = f"""pytest.assume({exp},"断言失败,表达式为:"{exp_final})"""
|
||
# pytest.assume(eval(exp),exp_reason)
|
||
eval(exp_final)
|
||
|
||
# 写的断言表达式里,没有使用jsonpath提取器.此时直接进行断言
|
||
else:
|
||
# 由于去掉了pytest.assume,所以先检测括号的完整性.
|
||
exp = self.__check_bracker(exp)
|
||
exp_final = f"pytest.assume({exp})"
|
||
exp_reason = f"断言失败,表达式为: {exp_final}"
|
||
pytest.assume(eval(exp),exp_reason)
|
||
|
||
# 判断是否为hamcrest 断言
|
||
elif expression.startswith(hamcrest_str):
|
||
# exp = expression.lstrip(hamcrest_str)
|
||
re_exp=f"^({hamcrest_str})"
|
||
exp = re.sub(re_exp,"",expression)
|
||
# 判断是否按要求使用了jsonpath 提取器
|
||
if jsonpath_str in exp:
|
||
# 去掉最外层的左右括号
|
||
exp = self.__remove_bracket(exp)
|
||
# 进行字段的切割
|
||
exp_list = exp.split(jsonpath_str)
|
||
# 获取jsonpath提取器的相关内容
|
||
exp2 = jsonpath_str + exp_list[1]
|
||
# 再检测左右括号的数量是否相等,不相等则进行完善
|
||
exp2 = self.__check_bracker(exp2)
|
||
exp2 = eval(exp2)
|
||
exp1 = exp_list[0]
|
||
|
||
# 先判断exp2 是否为str,若为str时,则要进行加上引号
|
||
if isinstance(exp2,str):
|
||
exp2 = f"'{exp2}'"
|
||
# 判断逗号是否存在于exp1中
|
||
if comma_str in exp1:
|
||
exp1_list = exp1.split(comma_str)
|
||
# 此时只要表达式写的正确,exp1_list的个数为2 ,暂时不考虑不为2的情况
|
||
exp1 = exp1_list[0]
|
||
exp2 = f"{exp1_list[1]}{exp2}"
|
||
# 再进行一次括号检查补全
|
||
exp2 = self.__check_bracker(exp2)
|
||
exp_final = f"assert_that({exp1},{exp2})"
|
||
exp_reason = f"断言失败,断言表达式为: {exp_final}"
|
||
print(f"使用jsonpath提取器提取数据后的表达式为(可能会出现缺少引号的情况):{exp_final}")
|
||
# 要转化为表达式
|
||
assert_that(eval(exp1),eval(exp2),reason=exp_reason)
|
||
|
||
# # 表示exp1里没有逗号,此时直接进行拼串
|
||
# else:
|
||
# exp = f"{exp1}{exp2}"
|
||
# # 再次检测括号是否完善
|
||
# exp = self.__check_bracker(exp)
|
||
# exp_final = f"assert_that({exp})"
|
||
# exp_reason = f"断言失败,表达式为: {exp_final}"
|
||
# print(f"使用jsonpath提取器提取数据后的表达式为(可能会出现缺少引号的情况):{exp_final}")
|
||
# assert_that(exp,reason=exp_reason)
|
||
|
||
# 表示exp1里没有逗号,表示表达式书写错误,暂时不考虑
|
||
else:
|
||
pytest.fail(f"表达式书写错误,断言方式,使用是hamcrest,该表达式里,应该有英文的逗号','但是未检测到.\n"
|
||
f"yml文件中的表达式为:{item}\n"
|
||
f"故将该用例置为fail")
|
||
|
||
# 表示没有按要求使用jsonpath 提取器
|
||
else:
|
||
# 判断是否有逗号存在
|
||
if comma_str in exp:
|
||
# 先去掉最外层的括号
|
||
exp = self.__remove_bracket(exp)
|
||
exp_list = exp.split(comma_str)
|
||
# 暂时不考虑切割后的个数不等于2的情况
|
||
exp1 = exp_list[0]
|
||
exp2 = exp_list[1]
|
||
# 检测括号的完整性
|
||
exp2 = self.__check_bracker(exp2)
|
||
exp_final = f"assert_that({exp1},{exp2})"
|
||
exp_reason = f"断言失败,表达式为: {exp_final}"
|
||
print(f"表达式为: {exp_final}")
|
||
assert_that(eval(exp1),eval(exp2),reason=exp_reason)
|
||
|
||
# 表示没有逗号,直接将该case置为fail
|
||
else:
|
||
pytest.fail(f"表达式书写错误,断言方式,使用是hamcrest,该表达式里,应该有英文的逗号','但是未检测到.\n"
|
||
f"yml文件中的表达式为:{item}\n"
|
||
f"故将该用例置为fail")
|
||
|
||
|
||
# 执行表达式, 经过多次的验证,不建议对表达式再次进行拼接,因为会存在出现值 没有引号等一系列的情况
|
||
# 所以最简单的方法,就是直接执行原始表达式.
|
||
|
||
# 表示使用的是其他的断言方式,本框架不支持
|
||
else:
|
||
print(f"本框架的断言,只支持pytest.assume() 和 hamcrest() 两种断言方式,且两种可混用。")
|
||
eval(item)
|
||
# 计数器加1
|
||
i+=1
|
||
print("\n")
|
||
|
||
|
||
else:
|
||
pytest.fail(f"由于assert 断言的格式书写错误,所以将该用例的运行结果置为:fail , 请参照样例重新填写.如下面的例子:\n"
|
||
f"api_data: \n"
|
||
f" .....\n"
|
||
f" assert:\n"
|
||
f" - pytest.assume(1<2)\n"
|
||
f" - assert_that(1,close_to(2,0.25))\n"
|
||
f"或assert: [pytest.assume(1<2),assert_that(1,close_to(2,0.25))]")
|
||
|
||
|
||
|
||
def __remove_bracket(self,exp:str)->str:
|
||
"""去掉表达式里的最外层的括号"""
|
||
left_bracket = "("
|
||
right_bracket = ")"
|
||
exp = exp
|
||
# 去年最外层的左右括号
|
||
exp = exp.lstrip(left_bracket).rstrip(right_bracket)
|
||
return exp
|
||
|
||
def __check_bracker(self, exp:str)->str:
|
||
"""检测表达式里的左右括号是否相等,若不相等,则进行补充完善,并返回"""
|
||
left_bracket = "("
|
||
right_bracket = ")"
|
||
# 获取各自的左右括号的数数量
|
||
left_count = exp.count(left_bracket)
|
||
right_count = exp.count(right_bracket)
|
||
value = left_count - right_count
|
||
if value == 0:
|
||
pass
|
||
|
||
elif value<0:
|
||
exp = left_bracket * abs(value) + exp
|
||
|
||
else:
|
||
exp = exp + right_bracket * value
|
||
|
||
return exp
|
||
|
||
|
||
|
||
|