根据:
from _ast import operator
from typing import Callable, Any
import jsonpath
from unit_tools.exceptions import AssertTypeError
class Assertions:
"""
接口断言模式封装
1) 状态码断言
2) 包含模式断言
3) 相等断言
4) 不相等断言
"""
@classmethod
def status_code_assert(cls, expected_result, status_code):
"""
接口的响应状态码断言
:param expected_result: (int)yaml文件code模式中的预期状态码
:param status_code: (int)接口实际返回的状态码
:return:
"""
# 断言状态标识,0 表示成功,其他表示失败
failure_count = 0
if not isinstance(expected_result, int):
expected_result = int(expected_result)
if expected_result == status_code:
print(f'状态吗断言成功:接口实际返回状态码{status_code} == {expected_result}')
else:
print(f'断言失败:接口实际返回状态码{status_code} != {expected_result}')
failure_count += 1
return failure_count
@classmethod
def contain_assert(cls, expected_result, response):
"""
字符串包含模式,断言预期结果字符串是否包含在接口的实际响应返回信息中
:param expected_result: (dict)yaml文件里面contain模式的数据
:param response: (dict)接口的实际响应信息
:return:
"""
# 断言状态标识,0 表示成功,其他表示失败
failure_count = 0
for assert_key, assert_value in expected_result.items():
response_list = jsonpath.jsonpath(response, f'$..{assert_key}')
if response_list and isinstance(response_list[0], str):
response_str = ''.join(response_list)
success_message = f"包含模式断言成功:预期结果【{assert_value}】存在于实际结果【{response_str}】中"
failure_message = f"包含模式断言失败:预期结果【{assert_value}】未在实际结果【{response_str}】中找到"
if assert_value in response_str:
print(success_message)
else:
failure_count = failure_count + 1
print(failure_message)
return failure_count
@classmethod
def equal_assert(cls, expected_result, response):
"""
相等断言,根据yaml里面的validation关键词下面的ne模式数据去跟接口实际响应信息对比
:param expected_result: (dict)yaml里面的eq值
:param response: (dict)接口实际响应结果
:return:
"""
failure_count = 0
if isinstance(response, dict) and isinstance(expected_result, dict):
# 找出实际结果与预期结果共同的key值
common_key = list(expected_result.keys() & response.keys())
if common_key:
# 找出实际结果与预期结果共同的key值
common_key = common_key[0]
new_actual_result = {common_key: response[common_key]}
eq_assert = operator.__eq__(new_actual_result, expected_result)
if eq_assert:
print(f"相等断言成功:接口实际结果{new_actual_result} == 预期结果:{expected_result}")
else:
failure_count += 1
print(f"相等断言失败:接口实际结果{new_actual_result} != 预期结果:{expected_result}")
else:
failure_count += 1
print('相等断言失败,请检查yaml文件eq模式的预期结果或接口返回值是否正确')
return failure_count
@classmethod
def assert_result(cls, expected_result, response, status_code):
"""
断言主函数,通过all_flag标记,如all_flag == 0表示测试成功,否则为失败
:param expected_result: (list)yaml文件validation关键词下面的预期结果
:param response: (dict)接口的实际响应信息
:param status_code: (int)接口的实际响应状态码
:return:
"""
all_flag = 0
# 通过字典映射方式管理不同的断言方式
assert_methods = {
'code': cls.status_code_assert,
'contain': cls.contain_assert,
'eq': cls.equal_assert
# 'ne': cls.not_equal_assert,
}
try:
for yq in expected_result:
for assert_mode, assert_value in yq.items():
# 表示assert_method是一个接受两个参数,类型为Any表示可以是任意类型,并返回整数的可调用对象
assert_method: Callable[[Any, Any], int] = assert_methods.get(assert_mode)
if assert_method:
# 调用对应的断言方法,传递适当的参数
if assert_mode in ['code']:
flag = assert_method(assert_value, status_code)
else:
flag = assert_method(assert_value, response)
all_flag += flag
else:
raise AssertTypeError(f'不支持{assert_mode}该断言模式')
except Exception as exceptions:
raise exceptions
assert all_flag == 0, '测试失败'
print('测试成功')
解决以下问题:
PS D:\a\b\PythonProject1> pytest -s
========================================================================= test session starts =========================================================================
platform win32 -- Python 3.11.9, pytest-7.4.0, pluggy-1.6.0
rootdir: D:\a\b\PythonProject1
configfile: pytest.ini
testpaths: test_test/
plugins: metadata-3.1.1, parallel-0.1.1, repeat-0.9.4, testreport-1.1.6, xdist-3.8.0
collecting ... D:\a\b\PythonProject1
{'api_name': '用户登录', 'url': 'https://10.121.177.71/itf/session', 'method': 'post', 'verify': 'verify', 'headers': {'Content-Type': 'application/x-www-form-urlencoded'}}
collected 3 items
test_test\test_api.py
请求头参数: {'Content-Type': 'application/x-www-form-urlencoded'}
参数类型:data
请求参数:{"username": "admin", "password": "admin"}
接口返回值: { "ResidueDay": 255, "ok": 0, "privilege": 4, "user_id": 2, "extendedpriv": 383, "racsession_id": 48, "remote_addr": "10.121.178.218", "server_name": "10.1
.71", "server_addr": "10.121.177.71", "HTTPSEnabled": 1, "CSRFToken": "SN3nPzFG", "channel": 1, "passwordStatus": 0, "encrypt_flag": 0, "SelfSetPriv": 1, "InfoQue e
ryPriv": 1, "DebugPriv": 1, "PowerConPriv": 1, "SecuConPriv": 1, "RemoteMediaPriv": 1, "RemoteKVMPriv": 1, "CommConfigPriv": 1, "UserConfigPriv": 1, "group_name": "Administrator", "ChangePasswordAlert": 0 }
状态吗断言成功:接口实际返回状态码200 == 200
测试成功
.
请求头参数: {'Content-Type': 'application/x-www-form-urlencoded'}
参数类型:data
请求参数:{"username": "admin", "password": "admin"}
接口返回值: { "ResidueDay": 255, "ok": 0, "privilege": 4, "user_id": 2, "extendedpriv": 383, "racsession_id": 49, "remote_addr": "10.121.178.218", "server_name": "10.1
21.177.71", "server_addr": "10.121.177.71", "HTTPSEnabled": 1, "CSRFToken": "uRz7lMlP", "channel": 1, "passwordStatus": 0, "encrypt_flag": 0, "SelfSetPriv": 1, "InfoQue
ryPriv": 1, "DebugPriv": 1, "PowerConPriv": 1, "SecuConPriv": 1, "RemoteMediaPriv": 1, "RemoteKVMPriv": 1, "CommConfigPriv": 1, "UserConfigPriv": 1, "group_name": "Administrator", "ChangePasswordAlert": 0 }
出现异常,--'NoneType' object is not iterable
F
请求头参数: {'Content-Type': 'application/x-www-form-urlencoded'}
参数类型:data
请求参数:{"username": "admin", "password": "admin"}
接口返回值: { "ResidueDay": 255, "ok": 0, "privilege": 4, "user_id": 2, "extendedpriv": 383, "racsession_id": 50, "remote_addr": "10.121.178.218", "server_name": "10.1
21.177.71", "server_addr": "10.121.177.71", "HTTPSEnabled": 1, "CSRFToken": "s4fPh5ra", "channel": 1, "passwordStatus": 0, "encrypt_flag": 0, "SelfSetPriv": 1, "InfoQue
ryPriv": 1, "DebugPriv": 1, "PowerConPriv": 1, "SecuConPriv": 1, "RemoteMediaPriv": 1, "RemoteKVMPriv": 1, "CommConfigPriv": 1, "UserConfigPriv": 1, "group_name": "Administrator", "ChangePasswordAlert": 0 }
出现异常,--'NoneType' object is not iterable
F
============================================================================== FAILURES ===============================================================================
________________________________________________________ TestLogin.test_login_module[api_info0-testcase0-2-3] _________________________________________________________
self = <test_test.test_api.TestLogin object at 0x00000198BD875810>
api_info = {'api_name': '用户登录', 'headers': {'Content-Type': 'application/x-www-form-urlencoded'}, 'method': 'post', 'url': 'https://10.121.177.71/itf/session', ...}
testcase = {'data': {'password': 'admin', 'username': 'admin'}}
@pytest.mark.passdef
@pytest.mark.repeat(3)
@pytest.mark.parametrize('api_info,testcase', YamlUtil().read_def_yaml('get_new.yaml'))
def test_login_module(self, api_info, testcase):
if 'case_name' not in testcase:
testcase['case_name'] = f"默认用例_{id(testcase)}" # 用唯一标识避免重复
> RequestsBase().execute_test_cases(api_info, testcase)
test_test\test_api.py:14:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
unit_tools\apiutils_single.py:121: in execute_test_cases
raise e
unit_tools\apiutils_single.py:117: in execute_test_cases
self.asserts.assert_result(validation,response.json(),status_code)
unit_tools\assertion_utils.py:124: in assert_result
raise exceptions
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
cls = <class 'unit_tools.assertion_utils.Assertions'>, expected_result = None
response = {'CSRFToken': 'uRz7lMlP', 'ChangePasswordAlert': 0, 'CommConfigPriv': 1, 'DebugPriv': 1, ...}, status_code = 200
@classmethod
def assert_result(cls, expected_result, response, status_code):
"""
断言主函数,通过all_flag标记,如all_flag == 0表示测试成功,否则为失败
:param expected_result: (list)yaml文件validation关键词下面的预期结果
:param response: (dict)接口的实际响应信息
:param status_code: (int)接口的实际响应状态码
:return:
"""
all_flag = 0
# 通过字典映射方式管理不同的断言方式
assert_methods = {
'code': cls.status_code_assert,
'contain': cls.contain_assert,
'eq': cls.equal_assert
# 'ne': cls.not_equal_assert,
}
try:
> for yq in expected_result:
E TypeError: 'NoneType' object is not iterable
unit_tools\assertion_utils.py:109: TypeError
________________________________________________________ TestLogin.test_login_module[api_info0-testcase0-3-3] _________________________________________________________
self = <test_test.test_api.TestLogin object at 0x00000198BD877F50>
api_info = {'api_name': '用户登录', 'headers': {'Content-Type': 'application/x-www-form-urlencoded'}, 'method': 'post', 'url': 'https://10.121.177.71/itf/session', ...}
testcase = {'data': {'password': 'admin', 'username': 'admin'}}
@pytest.mark.passdef
@pytest.mark.repeat(3)
@pytest.mark.parametrize('api_info,testcase', YamlUtil().read_def_yaml('get_new.yaml'))
def test_login_module(self, api_info, testcase):
if 'case_name' not in testcase:
testcase['case_name'] = f"默认用例_{id(testcase)}" # 用唯一标识避免重复
> RequestsBase().execute_test_cases(api_info, testcase)
test_test\test_api.py:14:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
unit_tools\apiutils_single.py:121: in execute_test_cases
raise e
unit_tools\apiutils_single.py:117: in execute_test_cases
self.asserts.assert_result(validation,response.json(),status_code)
unit_tools\assertion_utils.py:124: in assert_result
raise exceptions
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
cls = <class 'unit_tools.assertion_utils.Assertions'>, expected_result = None
response = {'CSRFToken': 's4fPh5ra', 'ChangePasswordAlert': 0, 'CommConfigPriv': 1, 'DebugPriv': 1, ...}, status_code = 200
@classmethod
def assert_result(cls, expected_result, response, status_code):
"""
断言主函数,通过all_flag标记,如all_flag == 0表示测试成功,否则为失败
:param expected_result: (list)yaml文件validation关键词下面的预期结果
:param response: (dict)接口的实际响应信息
:param status_code: (int)接口的实际响应状态码
:return:
"""
all_flag = 0
# 通过字典映射方式管理不同的断言方式
assert_methods = {
'code': cls.status_code_assert,
'contain': cls.contain_assert,
'eq': cls.equal_assert
# 'ne': cls.not_equal_assert,
}
try:
> for yq in expected_result:
E TypeError: 'NoneType' object is not iterable
unit_tools\assertion_utils.py:109: TypeError
========================================================================== warnings summary ===========================================================================
test_test/test_api.py::TestLogin::test_login_module[api_info0-testcase0-1-3]
test_test/test_api.py::TestLogin::test_login_module[api_info0-testcase0-2-3]
test_test/test_api.py::TestLogin::test_login_module[api_info0-testcase0-3-3]
D:\python\Lib\site-packages\urllib3\connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '10.121.177.71'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
warnings.warn(
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================================================================= short test summary info =======================================================================
FAILED test_test/test_api.py::TestLogin::test_login_module[api_info0-testcase0-2-3] - TypeError: 'NoneType' object is not iterable
FAILED test_test/test_api.py::TestLogin::test_login_module[api_info0-testcase0-3-3] - TypeError: 'NoneType' object is not iterable
=============================================================== 2 failed, 1 passed, 3 warnings in 2.34s ===============================================================
最新发布