修改用例格式由字典改为列表,并增加用例字段必填校验
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
## 一、框架介绍
|
## 一、框架介绍
|
||||||
|
|
||||||
本框架主要是基于 Python + pytest + Allure + loguru + 邮件通知/企业微信通知/钉钉通知 实现的接口自动化框架。
|
本框架主要是基于 Python + Pytest + Allure + Loguru + 邮件通知/企业微信通知/钉钉通知 实现的接口自动化框架。
|
||||||
|
|
||||||
* git地址: [https://www.gitlink.org.cn/floraachy/apiautotest](https://www.gitlink.org.cn/floraachy/apiautotest)
|
* git地址: [https://www.gitlink.org.cn/floraachy/apiautotest](https://www.gitlink.org.cn/floraachy/apiautotest)
|
||||||
* 项目参与者: floraachy
|
* 项目参与者: floraachy
|
||||||
@@ -193,8 +193,9 @@ case_common :公共参数
|
|||||||
allure_feature:用作于@allure.feature()装饰器中的内容。
|
allure_feature:用作于@allure.feature()装饰器中的内容。
|
||||||
allure_story:用作于@allure.story()装饰器中的内容。
|
allure_story:用作于@allure.story()装饰器中的内容。
|
||||||
case_markers: 给测试方法添加标记,支持自定义标记,skip, usefixtures。 格式是列表嵌套字符串或者字典。例如: # ['glcc', {'skip': '跳过执行该用例'}]
|
case_markers: 给测试方法添加标记,支持自定义标记,skip, usefixtures。 格式是列表嵌套字符串或者字典。例如: # ['glcc', {'skip': '跳过执行该用例'}]
|
||||||
case_001:用例ID
|
case_info: 具体的用例数据,是以列表的形式进行管理
|
||||||
feature:用例所属模块, 类似于@allure.feature()。
|
-
|
||||||
|
id:用例id
|
||||||
title:用例标题
|
title:用例标题
|
||||||
run:是否执行用例,为空或者True都会执行,为False则不执行。
|
run:是否执行用例,为空或者True都会执行,为False则不执行。
|
||||||
url:请求路径(可填写全路径 或者 资源路径)。通常我们填写资源路径。在用例执行前,会针对路径进行处理。具体可见case_utils.request_data_handle.py.RequestPreDataHandle.url_handle。注:请求路径=基准路径(host/base_url)+资源路径(url)。
|
url:请求路径(可填写全路径 或者 资源路径)。通常我们填写资源路径。在用例执行前,会针对路径进行处理。具体可见case_utils.request_data_handle.py.RequestPreDataHandle.url_handle。注:请求路径=基准路径(host/base_url)+资源路径(url)。
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ class CaseDataCheck:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def get_method(self) -> Text:
|
def get_method(self) -> Text:
|
||||||
|
"""
|
||||||
|
处理method参数:校验method参数是否是指定枚举值中的一个
|
||||||
|
"""
|
||||||
return self.check_params_right(
|
return self.check_params_right(
|
||||||
Method,
|
Method,
|
||||||
self.case_data.get(TestCaseEnum.METHOD.value[0])
|
self.case_data.get(TestCaseEnum.METHOD.value[0])
|
||||||
@@ -30,6 +32,9 @@ class CaseDataCheck:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def get_severity(self) -> Text:
|
def get_severity(self) -> Text:
|
||||||
|
"""
|
||||||
|
处理severity参数:如果Severity为空或者不传或者传错,视为NORMAL
|
||||||
|
"""
|
||||||
# 如果Severity为空或者不传或者传错,视为NORMAL
|
# 如果Severity为空或者不传或者传错,视为NORMAL
|
||||||
attr = self.case_data.get(TestCaseEnum.SEVERITY.value[0])
|
attr = self.case_data.get(TestCaseEnum.SEVERITY.value[0])
|
||||||
if attr is None or attr.upper() not in Severity._member_names_:
|
if attr is None or attr.upper() not in Severity._member_names_:
|
||||||
@@ -39,14 +44,20 @@ class CaseDataCheck:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def get_request_type(self):
|
def get_request_type(self):
|
||||||
|
"""
|
||||||
|
处理request_type参数:校验request_type参数是否是指定枚举值中的一个
|
||||||
|
"""
|
||||||
return self.check_params_right(
|
return self.check_params_right(
|
||||||
RequestType,
|
RequestType,
|
||||||
self.case_data.get(TestCaseEnum.REQUEST_TYPE.value[0])
|
self.case_data.get(TestCaseEnum.REQUEST_TYPE.value[0])
|
||||||
)
|
)
|
||||||
|
|
||||||
def check_case_data_attr(self, attr: Text):
|
def check_case_data_attr(self, attr: Text):
|
||||||
|
"""
|
||||||
|
检查用例中是否缺失某个参数
|
||||||
|
"""
|
||||||
assert attr in self.case_data.keys(), (
|
assert attr in self.case_data.keys(), (
|
||||||
f"用例ID为 {self.case_id} 的用例中缺少 {attr} 参数,请确认用例内容是否编写规范."
|
f"用例ID: {self.case_id} --> 缺少 {attr} 参数,请确认用例内容是否编写规范."
|
||||||
)
|
)
|
||||||
|
|
||||||
def check_params_exit(self):
|
def check_params_exit(self):
|
||||||
@@ -54,57 +65,59 @@ class CaseDataCheck:
|
|||||||
遍历一个枚举类中所有成员,并检查与每个成员对应的实例属性是否存在。
|
遍历一个枚举类中所有成员,并检查与每个成员对应的实例属性是否存在。
|
||||||
如果属性存在,则什么也不做,如果不存在,则抛出异常或执行其他操作
|
如果属性存在,则什么也不做,如果不存在,则抛出异常或执行其他操作
|
||||||
"""
|
"""
|
||||||
for enum in list(TestCaseEnum._value2member_map_.keys()):
|
print(f"打印:{list(TestCaseEnum._value2member_map_)}")
|
||||||
|
for enum in list(TestCaseEnum._value2member_map_):
|
||||||
if enum[1]:
|
if enum[1]:
|
||||||
self.check_case_data_attr(enum[0])
|
self.check_case_data_attr(enum[0])
|
||||||
|
|
||||||
|
def check_required_fields(self):
|
||||||
|
for field in TestCaseEnum:
|
||||||
|
if field.value[1]: # 判断是否为必填参数
|
||||||
|
field_name = field.value[0]
|
||||||
|
value = self.case_data[field_name]
|
||||||
|
if value is None or (isinstance(value, str) and len(value.strip()) == 0):
|
||||||
|
raise ValueError(f"用例ID: {self.case_id} --> {field_name}字段的值是:{value}, 该字段值是必填,请检查是否填写正确.")
|
||||||
|
|
||||||
def check_params_right(self, enum_name, attr):
|
def check_params_right(self, enum_name, attr):
|
||||||
"""
|
"""
|
||||||
检查参数值是否正确,符合要求规范
|
检查参数值是否正确,符合要求规范
|
||||||
"""
|
"""
|
||||||
_member_names_ = enum_name._member_names_
|
_member_names_ = enum_name._member_names_
|
||||||
assert attr.upper() in _member_names_, (
|
assert attr.upper() in _member_names_, (
|
||||||
f"用例ID为 {self.case_id} 的用例中 {enum_name}: {attr} 填写不正确,"
|
f"用例ID: {self.case_id} --> {enum_name}: {attr} 填写不正确,"
|
||||||
f"当前框架中只支持 {_member_names_} 类型."
|
f"当前框架中只支持 {_member_names_} 类型."
|
||||||
f"如有疑问,请联系管理员."
|
f"如有疑问,请联系管理员."
|
||||||
)
|
)
|
||||||
return attr.upper()
|
return attr.upper()
|
||||||
|
|
||||||
@property
|
|
||||||
def get_assert_response(self):
|
|
||||||
_assert_data = self.case_data.get(TestCaseEnum.ASSERT_RESPONSE.value[0])
|
|
||||||
assert _assert_data is not None, (
|
|
||||||
f"用例ID 为 {self.case_id} 未添加断言"
|
|
||||||
)
|
|
||||||
return _assert_data
|
|
||||||
|
|
||||||
def case_process(self, cases):
|
def case_process(self, cases):
|
||||||
case_list = []
|
case_list = []
|
||||||
for key, values in cases.items():
|
for key, values in cases.items():
|
||||||
# 公共配置中的数据,与用例数据不同,需要单独处理
|
# 公共配置中的数据,与用例数据不同,需要单独处理
|
||||||
if key != 'case_common':
|
if key == 'case_info':
|
||||||
# 检查用例数据,去除用例数据中的空格
|
for value in values:
|
||||||
for k, v in values.items():
|
self.case_data = value
|
||||||
values[k] = v.strip() if isinstance(v, str) else v
|
self.case_id = value.get("id")
|
||||||
self.case_data = values
|
# 检查用例参数,需要必填的是否有值
|
||||||
self.case_id = key
|
self.check_required_fields()
|
||||||
self.check_params_exit()
|
# 检查用例参数值,是否都填写正确
|
||||||
case_data = {
|
self.check_params_exit()
|
||||||
'feature': self.case_data.get(TestCaseEnum.FEATURE.value[0]),
|
case_data = {
|
||||||
'title': self.case_data.get(TestCaseEnum.TITLE.value[0]),
|
'id': self.case_data.get(TestCaseEnum.ID.value[0]),
|
||||||
'severity': self.get_severity,
|
'title': self.case_data.get(TestCaseEnum.TITLE.value[0]),
|
||||||
'url': self.case_data.get(TestCaseEnum.URL.value[0]),
|
'severity': self.get_severity,
|
||||||
'run': self.case_data.get(TestCaseEnum.RUN.value[0]),
|
'url': self.case_data.get(TestCaseEnum.URL.value[0]),
|
||||||
'method': self.get_method,
|
'run': self.case_data.get(TestCaseEnum.RUN.value[0]),
|
||||||
'headers': self.case_data.get(TestCaseEnum.HEADERS.value[0]),
|
'method': self.get_method,
|
||||||
'cookies': self.case_data.get(TestCaseEnum.COOKIES.value[0]),
|
'headers': self.case_data.get(TestCaseEnum.HEADERS.value[0]),
|
||||||
'request_type': self.get_request_type,
|
'cookies': self.case_data.get(TestCaseEnum.COOKIES.value[0]),
|
||||||
'payload': self.case_data.get(TestCaseEnum.PAYLOAD.value[0]),
|
'request_type': self.get_request_type,
|
||||||
'files': self.case_data.get(TestCaseEnum.FILES.value[0]),
|
'payload': self.case_data.get(TestCaseEnum.PAYLOAD.value[0]),
|
||||||
'extract': self.case_data.get(TestCaseEnum.EXTRACT.value[0]),
|
'files': self.case_data.get(TestCaseEnum.FILES.value[0]),
|
||||||
"assert_response": self.get_assert_response,
|
'extract': self.case_data.get(TestCaseEnum.EXTRACT.value[0]),
|
||||||
"assert_sql": self.case_data.get(TestCaseEnum.ASSERT_SQL.value[0]),
|
"assert_response": self.case_data.get(TestCaseEnum.ASSERT_RESPONSE.value[0]),
|
||||||
}
|
"assert_sql": self.case_data.get(TestCaseEnum.ASSERT_SQL.value[0]),
|
||||||
case_list.append(TestCase(**case_data).dict())
|
}
|
||||||
|
case_list.append(case_data)
|
||||||
|
|
||||||
return case_list
|
return case_list
|
||||||
|
|||||||
@@ -57,8 +57,7 @@ def handle_excel_data(file):
|
|||||||
"allure_story": v["sheet_name"]
|
"allure_story": v["sheet_name"]
|
||||||
}
|
}
|
||||||
# 处理excel用例数据
|
# 处理excel用例数据
|
||||||
for case in v["data"]:
|
excel_case["case_info"] = v["data"]
|
||||||
excel_case[case["id"]] = case
|
|
||||||
# 检查用例数据是否符合规范
|
# 检查用例数据是否符合规范
|
||||||
tested_case = CaseDataCheck().case_process(excel_case)
|
tested_case = CaseDataCheck().case_process(excel_case)
|
||||||
# 生成测试方法
|
# 生成测试方法
|
||||||
|
|||||||
+2
-2
@@ -76,7 +76,7 @@ class TestCaseEnum(Enum):
|
|||||||
"""
|
"""
|
||||||
测试用例中字段
|
测试用例中字段
|
||||||
"""
|
"""
|
||||||
FEATURE = ("feature", False)
|
ID = ("id", True)
|
||||||
TITLE = ("title", True)
|
TITLE = ("title", True)
|
||||||
URL = ("url", True)
|
URL = ("url", True)
|
||||||
SEVERITY = ("severity", False)
|
SEVERITY = ("severity", False)
|
||||||
@@ -96,7 +96,7 @@ class TestCase(BaseModel):
|
|||||||
"""
|
"""
|
||||||
测试用例各数据格式要求
|
测试用例各数据格式要求
|
||||||
"""
|
"""
|
||||||
feature: Union[None, Text] = None
|
id: Union[None, Text]
|
||||||
title: Text
|
title: Text
|
||||||
severity: Text
|
severity: Text
|
||||||
url: Text
|
url: Text
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
# ------------------------------------ 配置信息 ----------------------------------------------------#
|
# ------------------------------------ 配置信息 ----------------------------------------------------#
|
||||||
# 0代表执行Excel和yaml两种格式的用例, 1 代表 yaml文件,2 用例代表Excel用例, 其他数值将不自动生成用例,仅能执行手动编写的用例
|
# 0代表执行Excel和yaml两种格式的用例, 1 代表 yaml文件,2 用例代表Excel用例, 其他数值将不自动生成用例,仅能执行手动编写的用例
|
||||||
CASE_FILE_TYPE = 1
|
CASE_FILE_TYPE = 0
|
||||||
|
|
||||||
# 0表示默认不发送任何通知, 1代表钉钉通知,2代表企业微信通知, 3代表邮件通知, 4代表所有途径都发送通知
|
# 0表示默认不发送任何通知, 1代表钉钉通知,2代表企业微信通知, 3代表邮件通知, 4代表所有途径都发送通知
|
||||||
SEND_RESULT_TYPE = 0
|
SEND_RESULT_TYPE = 0
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ case_common:
|
|||||||
- get_project
|
- get_project
|
||||||
- skip: 跳过执行该用例
|
- skip: 跳过执行该用例
|
||||||
|
|
||||||
case_glcc_demo_01:
|
case_info:
|
||||||
feature: GLCC
|
-
|
||||||
|
id: case_glcc_demo_01
|
||||||
title: 获取已报名成功的项目数据
|
title: 获取已报名成功的项目数据
|
||||||
severity: normal
|
severity: normal
|
||||||
run: True
|
run: True
|
||||||
@@ -26,8 +27,8 @@ case_glcc_demo_01:
|
|||||||
$.message: success
|
$.message: success
|
||||||
assert_sql:
|
assert_sql:
|
||||||
|
|
||||||
case_glcc_demo_02:
|
-
|
||||||
feature: GLCC
|
id: case_glcc_demo_02
|
||||||
title: 获取已报名成功的课题数据
|
title: 获取已报名成功的课题数据
|
||||||
severity: normal
|
severity: normal
|
||||||
run: True
|
run: True
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ case_common:
|
|||||||
- skip: 跳过执行该用例
|
- skip: 跳过执行该用例
|
||||||
|
|
||||||
# 用例数据
|
# 用例数据
|
||||||
case_login_demo_01:
|
case_info:
|
||||||
feature: 登录
|
-
|
||||||
|
id: case_login_01
|
||||||
title: 用户名密码正确,登录成功(不校验数据库)
|
title: 用户名密码正确,登录成功(不校验数据库)
|
||||||
run: True
|
run: True
|
||||||
severity: normal
|
severity: normal
|
||||||
@@ -39,8 +40,8 @@ case_login_demo_01:
|
|||||||
$.user_id: 85390
|
$.user_id: 85390
|
||||||
assert_sql:
|
assert_sql:
|
||||||
|
|
||||||
case_login_demo_02:
|
-
|
||||||
feature: 登录
|
id: case_login_02
|
||||||
title: 用户名密码正确,登录成功(校验数据库)
|
title: 用户名密码正确,登录成功(校验数据库)
|
||||||
run: False
|
run: False
|
||||||
severity: minor
|
severity: minor
|
||||||
@@ -71,8 +72,9 @@ case_login_demo_02:
|
|||||||
eq:
|
eq:
|
||||||
sql: select count(*) from tokens where user_id=${user_id};
|
sql: select count(*) from tokens where user_id=${user_id};
|
||||||
len: 1
|
len: 1
|
||||||
case_login_demo_03:
|
|
||||||
feature: 登录
|
-
|
||||||
|
id: case_login_03
|
||||||
title: 用户名正确,密码错误,登录失败
|
title: 用户名正确,密码错误,登录失败
|
||||||
severity: critical
|
severity: critical
|
||||||
run: False
|
run: False
|
||||||
|
|||||||
Binary file not shown.
@@ -9,8 +9,9 @@ case_common:
|
|||||||
- usefixtures: login_init
|
- usefixtures: login_init
|
||||||
|
|
||||||
# 用例数据
|
# 用例数据
|
||||||
case_new_project_demo_01:
|
case_info:
|
||||||
feature: 新建项目
|
-
|
||||||
|
id: case_new_project_01
|
||||||
title: 正确输入各项必填参数,新建项目成功(不校验数据库, header里面传cookies)
|
title: 正确输入各项必填参数,新建项目成功(不校验数据库, header里面传cookies)
|
||||||
severity: critical
|
severity: critical
|
||||||
run: True
|
run: True
|
||||||
@@ -36,8 +37,8 @@ case_new_project_demo_01:
|
|||||||
$.login: ${login}
|
$.login: ${login}
|
||||||
assert_sql:
|
assert_sql:
|
||||||
|
|
||||||
case_new_project_demo_02:
|
-
|
||||||
feature: 新建项目
|
id: case_new_project_02
|
||||||
title: 正确输入各项必填参数,新建项目成功(不校验数据库,单独传cookies)
|
title: 正确输入各项必填参数,新建项目成功(不校验数据库,单独传cookies)
|
||||||
run: True
|
run: True
|
||||||
url: /api/projects.json
|
url: /api/projects.json
|
||||||
@@ -61,8 +62,8 @@ case_new_project_demo_02:
|
|||||||
$.login: ${login}
|
$.login: ${login}
|
||||||
assert_sql:
|
assert_sql:
|
||||||
|
|
||||||
case_new_project_demo_03:
|
-
|
||||||
feature: 新建项目
|
id: case_new_project_03
|
||||||
title: 正确输入各项必填参数,新建项目成功(校验数据库)
|
title: 正确输入各项必填参数,新建项目成功(校验数据库)
|
||||||
severity: normal
|
severity: normal
|
||||||
run: False
|
run: False
|
||||||
@@ -91,8 +92,8 @@ case_new_project_demo_03:
|
|||||||
$.name: ${project_name}
|
$.name: ${project_name}
|
||||||
$.identifier: ${project_identifier}
|
$.identifier: ${project_identifier}
|
||||||
|
|
||||||
case_new_project_demo_04:
|
-
|
||||||
feature: 新建项目
|
id: case_new_project_04
|
||||||
title: 正确输入各项必填参数,新建项目成功(04)
|
title: 正确输入各项必填参数,新建项目成功(04)
|
||||||
severity: normal
|
severity: normal
|
||||||
run: False
|
run: False
|
||||||
@@ -117,8 +118,8 @@ case_new_project_demo_04:
|
|||||||
$.login: ${login}
|
$.login: ${login}
|
||||||
assert_sql:
|
assert_sql:
|
||||||
|
|
||||||
case_new_project_demo_05:
|
-
|
||||||
feature: 新建项目
|
id: case_new_project_05
|
||||||
title: 正确输入各项必填参数,新建项目成功(05)
|
title: 正确输入各项必填参数,新建项目成功(05)
|
||||||
severity: normal
|
severity: normal
|
||||||
run: False
|
run: False
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ case_common:
|
|||||||
- skip: 跳过该用例
|
- skip: 跳过该用例
|
||||||
|
|
||||||
# 用例数据
|
# 用例数据
|
||||||
case_upload_demo_01:
|
case_info:
|
||||||
feature: 上传文件
|
-
|
||||||
|
id: case_upload_file_01
|
||||||
title: 测试单文件上传
|
title: 测试单文件上传
|
||||||
severity:
|
severity:
|
||||||
run: False
|
run: False
|
||||||
@@ -30,8 +31,8 @@ case_upload_demo_01:
|
|||||||
http_code: 200
|
http_code: 200
|
||||||
assert_sql:
|
assert_sql:
|
||||||
|
|
||||||
case_upload_demo_02:
|
-
|
||||||
feature: 上传文件
|
id: case_upload_file_02
|
||||||
title: 测试多文件上传(该接口不支持多文件上传,这是一个示例)
|
title: 测试多文件上传(该接口不支持多文件上传,这是一个示例)
|
||||||
severity: normal
|
severity: normal
|
||||||
run: False
|
run: False
|
||||||
|
|||||||
+3
-2
@@ -6,8 +6,9 @@ case_common:
|
|||||||
case_markers:
|
case_markers:
|
||||||
|
|
||||||
# 用例数据
|
# 用例数据
|
||||||
case_demo_01:
|
case_info:
|
||||||
feature: debug
|
-
|
||||||
|
id: case_demo_01
|
||||||
title: 这是一条用于调试功能的用例
|
title: 这是一条用于调试功能的用例
|
||||||
run: True
|
run: True
|
||||||
severity: normal
|
severity: normal
|
||||||
|
|||||||
@@ -24,10 +24,7 @@ from case_utils.allure_handle import allure_title
|
|||||||
# 读取用例数据
|
# 读取用例数据
|
||||||
yaml_data = YamlHandle(filename=os.path.join(DATA_DIR, "gitlink", "login_demo.yaml")).read_yaml
|
yaml_data = YamlHandle(filename=os.path.join(DATA_DIR, "gitlink", "login_demo.yaml")).read_yaml
|
||||||
case_common = yaml_data["case_common"]
|
case_common = yaml_data["case_common"]
|
||||||
cases = []
|
cases = yaml_data["case_info"]
|
||||||
for k, v in yaml_data.items():
|
|
||||||
if k != "case_common":
|
|
||||||
cases.append(v)
|
|
||||||
|
|
||||||
|
|
||||||
@allure.epic(case_common["allure_epic"])
|
@allure.epic(case_common["allure_epic"])
|
||||||
|
|||||||
Reference in New Issue
Block a user