pytest_ui_api_fw/common/util/auto_assert.py

292 lines
14 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
# 设置utf-8 显示中文
"""
@Author: guo
@Fileauto_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