api_keyword
api_keyword.py
# -*- coding: utf-8 -*-
# @Time : 2023/11/5 20:25
# @Author : Hami
import allure
import requests
import jsonpath
import json
from deepdiff import DeepDiff
from day09.P02_ApiFrameProject.config import *
# 关键字驱动/基类/工具类
# 1. 发送请求:8种:post、get、put、delete...
# 2. 提取数据
# 补充知识点:方法的缺省值:params=None (默认值),如果没有传参则默认为None
class ApiKey:
@allure.step(">>>>>>:开始发送Get请求")
def get(self, url, params=None, **kwargs):
"""
发送get请求
:param url:接口请求url
:param params: 拼接在url的参数
:param kwargs: 其它的参数
:return: 返回请求后的数据
"""
# print(">>>>>>:开始发送Get请求")
return requests.get(url=url, params=params, **kwargs)
@allure.step(">>>>>>:开始发送Post请求")
def post(self, url, data=None, json=None, **kwargs):
"""
发送post请求
:param url: 接口请求url
:param data: data的请求数据
:param json: json的请求数据
:param kwargs: 其它的参数
:return: 返回请求后的数据
"""
# print(">>>>>>:开始发送Post请求")
res = requests.post(url=url, data=data, json=json, **kwargs)
print(">>>>>>:响应数据为:", res.json())
return res
@allure.step(">>>>>>:开始提取JsonPath数据")
def get_text(self, response, key):
"""
提取json当中的某个值
:param response: 需要提取的json数据,比如:{"msg":"登录成功"}
:param key: 对应要提取的jsonpath,比如: $.msg
:return: 返回提取数据之后的【第一个值】
"""
if isinstance(response, str):
# 是字符串,我就让它转一下类型
response = json.loads(response)
print(">>>>>>:开始提取JsonPath数据")
value_list = jsonpath.jsonpath(response, key)
print(">>>>>>:提取数据为:", value_list[0])
return value_list[0]
@allure.step(">>>>>>:开始提取数据库的数据")
def get_sqlData(self, sqlValue):
"""
:param sqlValue: SQL,返回的数据是一个元组
:return:
"""
import pymysql
# 1. 配置数据库连接信息并连接
connection = pymysql.connect(
host=DB_HOST, # 数据库地址
port=DB_PORT,
user=DB_USER, # 数据库用户名
password=DB_PASSWORD, # 数据库密码
db=DB_NAME, # 数据库名称
)
# 2. 创建游标对象,使用它进行操作
cursor = connection.cursor()
# 4. 使用游标对象去执行操作SQL
cursor.execute(sqlValue)
# 5. 得到结果集的下一行
result = cursor.fetchone()
# 6. 关闭数据库连接
cursor.close()
return result[0]
@allure.step(">>>>>>:响应数据全量对比")
def jsonDeepDiff(self, json1, json2, **other):
"""
对比json数据的一致性
:param json1: 期望结果
:param json2: 实际结果
:param other: 你想要写的对应的规则
:return:
"""
res = DeepDiff(json1, json2, **other)
if res == {}:
return True
else:
return False
# 函数的入口:main
if __name__ == '__main__':
# 1. 实例化对象:ApiKey
ak = ApiKey()
# 2. 通过对应的类调用对应的方法 --四要素
# url = "http://shop-xo.hctestedu.com/index.php?s=/api/user/login"
# pulic_data = {"application": "app", "application_client_type": "weixin"}
# # 请求参数-- body (你的body数据是要以json进行提交,参数:json)
# data = {"accounts": "hami", "pwd": "123456", "type": "username"}
#
# res = ak.post(url=url, params=pulic_data, data=data)
# # 3. 提取数据
# text = ak.get_text(res.json(), "$.msg")
# 案例2 :"{"msg":"登录成功"}"
res = '{"msg":"登录成功"}'
text = ak.get_text(res, "$.msg")
common
EncryptDateAES.py
# -*- coding: utf-8 -*-
# @Time : 2023/11/3 21:23
# @Author : Hami
"""
对称加密:加密和解密使用的是同一把钥匙,即:使用相同的密匙对同一密码进行加密和解密。
常用的加密方法:DES、3DES、AES...(AES算法是目前最常用的对称加密算法)
"""
import base64
from Crypto.Cipher import AES
class EncryptDate:
# 构造方法
def __init__(self, key):
# 类属性
self.key = key.encode("utf-8") # 初始化密钥
self.length = AES.block_size # 初始化数据块大小 :16位
self.aes = AES.new(self.key, AES.MODE_ECB) # 初始化AES,ECB模式的实例
# 截断函数,去除填充的字符
self.unpad = lambda date: date[0:-ord(date[-1])]
# 缺几位数据就补齐多少位数据:16的倍数
def pad(self, text): # text == tony
"""
#填充函数,使被加密数据的字节码长度是block_size的整数倍
"""
count = len(text.encode('utf-8')) # count = 4
add = self.length - (count % self.length) # 求它们相差的位数
# add = 16- (4%16) === 16 - 4 == 12
entext = text + (chr(add) * add)
# entext = “tony” + (chr(add) * 12 ) === entext == tony
# print("entext的数据是:",entext)
return entext
# 加密函数
def encrypt(self, encrData): # 加密函数 encrData == tony (16位)
res = self.aes.encrypt(self.pad(encrData).encode("utf8")) # self.aes.encrypt(tony)
msg = str(base64.b64encode(res), encoding="utf8")
return msg
# 解密函数
def decrypt(self, decrData): # 解密函数 XbXHJrNLwoTVcyfqM9eTgQ==
# 从base64编码转回来
res = base64.decodebytes(decrData.encode("utf8"))
# 将数据进行对应的解密:XbXHJrNLwoTVcyfqM9eTgQ==
msg = self.aes.decrypt(res).decode("utf8")
# print("msg的值:",msg)
# 把转回来的数据后面的字符去掉。
return self.unpad(msg)
key = "1234567812345678"
ENCRYPTAES = EncryptDate(key)
FileDataDriver.py
# -*- coding: utf-8 -*-
# @Time : 2023/11/8 21:01
# @Author : Hami
import openpyxl
from day09.P02_ApiFrameProject.config import *
import yaml
from day09.P02_ApiFrameProject.common.EncryptDateAES import ENCRYPTAES
class FileReader:
"""
专门用来读取和写入yaml、excel文件
"""
# 读取excel--openpyxl -- 文件格式:.xlsx
@staticmethod # 直接通过类名进行调用
def read_excel(file_path=CASEDATAURL, sheet_name=SHEETNAME):
"""
读取Excel文件,只支持 .xlsx文件
:param file_path: 文件路径
:return: excel文件数据,元组的格式
"""
# 打开现有的Excel文件或创建新的文件
try:
# 正常情况下直接打开
workbook = openpyxl.load_workbook(file_path)
except FileNotFoundError:
workbook = openpyxl.Workbook()
# 选择或创建指定的工作表
if sheet_name in workbook.sheetnames:
# 【正常】 判断有没有对应的shtttname ,有的话把对应的数据给我加载出来
worksheet = workbook[sheet_name]
else:
# 没有的话,给我新建一个
worksheet = workbook.create_sheet(sheet_name)
# 获取列名 --- 把第2行的数据拿出来作为我们的key值
headers = [cell.value for cell in worksheet[2]]
# print("所有的key", headers)
# 将数据存储为字典,并且放在我们data当中
data = [] # 所有的数据
# 把小的数据从第三行开始
for row in worksheet.iter_rows(min_row=3, values_only=True):
# 把所有的数据直接加进去
# data.append(dict(zip(headers, row)))
# 当 is_true == True才应该加进去
new_data = dict(zip(headers, row))
if new_data["is_true"] is True:
data.append(new_data)
workbook.close()
# 所有的数据
return data
@staticmethod
def writeDataToExcel(file_path=CASEDATAURL, sheet_name=SHEETNAME, row=None, column=None, value=None):
# 打开现有的Excel文件或创建新的文件
try:
workbook = openpyxl.load_workbook(file_path)
except FileNotFoundError:
workbook = openpyxl.Workbook()
# 选择或创建指定的工作表
if sheet_name in workbook.sheetnames:
worksheet = workbook[sheet_name]
else:
worksheet = workbook.create_sheet(sheet_name)
# 写入数据到指定行和列
worksheet.cell(row=row, column=column).value = value
# 保存修改后的文件--- 所以执行过程当中excel是要关闭的状态
workbook.save(file_path)
@staticmethod
def read_yaml(file_path=YAMLDATA):
"""
读取yaml文件
:param file_path: 文件路径
:return: yaml文件数据
"""
with open(file_path, 'r', encoding="utf-8") as file:
data = yaml.safe_load(file)
return data
@staticmethod
def write_yaml(data, file_path=YAMLDATA):
"""
写入yaml文件,写入无序没有关系,通过key获取数据
:param data: 需要写入的数据
:param file_path: 文件路径
:return:
"""
with open(file_path, 'w', encoding="utf-8") as file:
# allow_unicode=True,避免将中文字符转换为 Unicode 转义序列
yaml.dump(data, file, allow_unicode=True)
@staticmethod
def data_EncryptDateAES(data):
newdata = {} # 去掉前面@符号同时数据进行加密
for key in data:
if key[0] == "@":
# 需要加密处理
newdata[key[1:]] = ENCRYPTAES.encrypt(data[key])
else:
# 不需要加密处理
newdata[key] = data[key]
return newdata
if __name__ == '__main__':
# CaseData = FileReader.read_excel()
# 需要加密处理
data = {"@username": "tony", "@password": "123456"}
res = FileReader.data_EncryptDateAES(data)
print(res)
# 不需要加密处理
data = {"accounts": "hami", "@pwd": "123456", "type": "username"}
res = FileReader.data_EncryptDateAES(data)
print(res)
data
excel
编号[0] | 地址[1] | 路径[2] | 请求方法[3] | 公共参数(PARAMS)[4] | 请求头[5] | 参数[6] | 参数类型[7] | 校验字段[8] | 预期结果[9] | 检查结果[10] | 用例名[11] | JSON提取_引用名称[12] | SQL提取[13] | 正则提取_引用名称[14] | story(小模块)[16] | feature(大模块)[17] | 备注[18] | 级别[19] | SQL校验字段名[20] 【列表格式】 | SQL期望数据[22]【集合格式】 | 响应全量比对[25] | 响应全量条件[26] | 是否执行 |
id | url | path | method | params | headers | data | type | actualResult | expectResult | result | caseName | jsonExData | sqlExData | regularExData | storyName | featureName | remark | rank | sqlAssertData | sqlExpectResult | responseExpect | responseExclude | is_true |
3 | http://shop-xo.hctestedu.com/index.php?s= | api/user/reg | post | {"application": "app", "application_client_type": "weixin"} | {"accounts":"hami","pwd":"123456","type": "username"} | data | $.msg | 注册成功 | 测试失败,断言失败。 | 注册成功 | F01S01_注册 | F01_用户管理 | 用例注册 | blocker | TRUE | ||||||||
yaml
- actualResult: $..msg
caseName: 成功调用登陆接口,msg返回登录成功
data:
accounts: hami
pwd: 123456
type: username
expectResult: 登录成功
featureName: F01_用户管理
headers: null
id: 0
jsonExData:
MSG: $.msg
VAR_TOKEN: $..data.token
method: post
params:
application: app
application_client_type: weixin
path: api/user/login
rank: blocker
regularExData: null
remark: 正常用例登陆
result: 测试通过,断言成功。
sqlAssertData:
id: SELECT id FROM sxo_user WHERE username='hami'
name: SELECT username FROM sxo_user WHERE username='hami'
sqlExData:
id: SELECT id FROM sxo_user WHERE username='hami'
name: SELECT username FROM sxo_user WHERE username='hami'
sqlExpectResult:
id: 65
name: hami
storyName: F01S01_登陆
type: data
url: http://shop-xo.hctestedu.com/index.php?s=
- actualResult: $..msg
caseName: 加入购物车
data:
goods_id: 10
spec: null
stock: 1
expectResult: 加入成功
featureName: F01_购物车管理
headers:
Content-Type: application/json;charset=UTF-8
id: 1
jsonExData: null
method: post
params:
application: app
application_client_type: weixin
token: 19182bcffd67ef89fae61d09da16d2fd
path: api/cart/save
rank: blocker
regularExData: null
remark: 能够正确的加入购物车
result: 测试通过,断言成功。
sqlAssertData: null
sqlExData: null
sqlExpectResult: null
storyName: F02S01_加入购物车
type: json
url: http://shop-xo.hctestedu.com/index.php?s=
- actualResult: $..msg
caseName: 错误的密码进行登录
data:
accounts: hami
pwd: 4564641
type: username
expectResult: 密码错误
featureName: F01_用户管理
headers: null
id: 2
jsonExData: null
method: post
params:
application: app
application_client_type: weixin
path: api/user/login
rank: blocker
regularExData: null
remark: 正常用例登陆
result: 测试通过,断言成功。
sqlAssertData: null
sqlExData: null
sqlExpectResult: null
storyName: F01S01_登陆
type: data
url: http://shop-xo.hctestedu.com/index.php?s=
logdata
log.log
2023-11-15 21:26:05 INFO 用例ID:testcase/Test_Excel_08.py::TestCase::testCase[CaseData0] conftest.py pytest_runtest_makereport 47
2023-11-15 21:26:05 INFO 测试结果:failed conftest.py pytest_runtest_makereport 48
2023-11-15 21:26:05 INFO 故障表示:self = <Test_Excel_08.TestCase object at 0x000001D249B9B9A0>
CaseData = {'actualResult': '$.msg', 'caseName': '注册成功', 'data': '{"accounts":"hami","pwd":"123456","type": "username"}', 'expectResult': '注册成功', ...}
@pytest.mark.flaky(reruns=3, reruns_delay=3)
@pytest.mark.parametrize("CaseData", AllCaseData)
def testCase(self, CaseData):
self.__dynamic_title(CaseData)
CaseData = eval(Template(str(CaseData)).render(self.all_var))
print(CaseData)
# 写Excle的行和列
row = CaseData["id"]
column = 11
# 初始化对应的值:
res = None
msg = None
value = None
# -------------------------发送请求-------------------------------
try:
# 请求数据
url = CaseData["url"] + CaseData["path"]
params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else None
headers = eval(CaseData["headers"]) if isinstance(CaseData["headers"], str) else None
if CaseData["data"] is None: # 会影响框架的性能
data = None
else:
# 进行加密处理
data = FileReader.data_EncryptDateAES(eval(CaseData["data"]))
dict_data = {
"url": url,
"params": params,
"headers": headers,
"data": data
}
if CaseData["type"] == "json":
dict_data["data"] = json.dumps(dict_data["data"])
except Exception:
value = MSG_DATA_ERROR
FileReader.writeDataToExcel(row=row, column=column, value=value)
else: # 响应对象
# 得到对应的响应数据
res = getattr(self.ak, CaseData["method"])(**dict_data)
# -------------------------提取数据库的操作---------------------------
self.__sql_extraction(CaseData)
# -------------------------进行断言处理-------------------------------
# 实际结果
try:
msg = self.ak.get_text(res.json(), CaseData["actualResult"])
except Exception:
value = MSG_EXDATA_ERROR
FileReader.writeDataToExcel(row=row, column=column, value=value)
else:
# 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
if msg == CaseData["expectResult"]:
value = MSG_ASSERT_OK
# 成功之后进行数据提取
self.__json_extraction(CaseData, res)
else:
value = MSG_ASSERT_NO
FileReader.writeDataToExcel(row=row, column=column, value=value)
finally:
> assert msg == CaseData["expectResult"], value
E AssertionError: 测试失败,断言失败。
E assert '账号已存在' == '注册成功'
E - 注册成功
E + 账号已存在
testcase\Test_Excel_08.py:181: AssertionError conftest.py pytest_runtest_makereport 49
2023-11-15 21:26:05 INFO 异常:<ExceptionInfo AssertionError("测试失败,断言失败。\nassert '账号已存在' == '注册成功'\n - 注册成功\n + 账号已存在") tblen=16> conftest.py pytest_runtest_makereport 50
2023-11-15 21:26:05 INFO 用例耗时:0.23679439999999996 conftest.py pytest_runtest_makereport 51
2023-11-15 21:26:05 INFO ************************************** conftest.py pytest_runtest_makereport 52
2023-11-15 21:26:08 INFO 用例ID:testcase/Test_Excel_08.py::TestCase::testCase[CaseData0] conftest.py pytest_runtest_makereport 47
2023-11-15 21:26:08 INFO 测试结果:failed conftest.py pytest_runtest_makereport 48
2023-11-15 21:26:08 INFO 故障表示:self = <Test_Excel_08.TestCase object at 0x000001D249B9B9A0>
CaseData = {'actualResult': '$.msg', 'caseName': '注册成功', 'data': '{"accounts":"hami","pwd":"123456","type": "username"}', 'expectResult': '注册成功', ...}
@pytest.mark.flaky(reruns=3, reruns_delay=3)
@pytest.mark.parametrize("CaseData", AllCaseData)
def testCase(self, CaseData):
self.__dynamic_title(CaseData)
CaseData = eval(Template(str(CaseData)).render(self.all_var))
print(CaseData)
# 写Excle的行和列
row = CaseData["id"]
column = 11
# 初始化对应的值:
res = None
msg = None
value = None
# -------------------------发送请求-------------------------------
try:
# 请求数据
url = CaseData["url"] + CaseData["path"]
params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else None
headers = eval(CaseData["headers"]) if isinstance(CaseData["headers"], str) else None
if CaseData["data"] is None: # 会影响框架的性能
data = None
else:
# 进行加密处理
data = FileReader.data_EncryptDateAES(eval(CaseData["data"]))
dict_data = {
"url": url,
"params": params,
"headers": headers,
"data": data
}
if CaseData["type"] == "json":
dict_data["data"] = json.dumps(dict_data["data"])
except Exception:
value = MSG_DATA_ERROR
FileReader.writeDataToExcel(row=row, column=column, value=value)
else: # 响应对象
# 得到对应的响应数据
res = getattr(self.ak, CaseData["method"])(**dict_data)
# -------------------------提取数据库的操作---------------------------
self.__sql_extraction(CaseData)
# -------------------------进行断言处理-------------------------------
# 实际结果
try:
msg = self.ak.get_text(res.json(), CaseData["actualResult"])
except Exception:
value = MSG_EXDATA_ERROR
FileReader.writeDataToExcel(row=row, column=column, value=value)
else:
# 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
if msg == CaseData["expectResult"]:
value = MSG_ASSERT_OK
# 成功之后进行数据提取
self.__json_extraction(CaseData, res)
else:
value = MSG_ASSERT_NO
FileReader.writeDataToExcel(row=row, column=column, value=value)
finally:
> assert msg == CaseData["expectResult"], value
E AssertionError: 测试失败,断言失败。
E assert '账号已存在' == '注册成功'
E - 注册成功
E + 账号已存在
testcase\Test_Excel_08.py:181: AssertionError conftest.py pytest_runtest_makereport 49
2023-11-15 21:26:08 INFO 异常:<ExceptionInfo AssertionError("测试失败,断言失败。\nassert '账号已存在' == '注册成功'\n - 注册成功\n + 账号已存在") tblen=16> conftest.py pytest_runtest_makereport 50
2023-11-15 21:26:08 INFO 用例耗时:0.20338590000000067 conftest.py pytest_runtest_makereport 51
2023-11-15 21:26:08 INFO ************************************** conftest.py pytest_runtest_makereport 52
2023-11-15 21:26:11 INFO 用例ID:testcase/Test_Excel_08.py::TestCase::testCase[CaseData0] conftest.py pytest_runtest_makereport 47
2023-11-15 21:26:11 INFO 测试结果:failed conftest.py pytest_runtest_makereport 48
2023-11-15 21:26:11 INFO 故障表示:self = <Test_Excel_08.TestCase object at 0x000001D249B9B9A0>
CaseData = {'actualResult': '$.msg', 'caseName': '注册成功', 'data': '{"accounts":"hami","pwd":"123456","type": "username"}', 'expectResult': '注册成功', ...}
@pytest.mark.flaky(reruns=3, reruns_delay=3)
@pytest.mark.parametrize("CaseData", AllCaseData)
def testCase(self, CaseData):
self.__dynamic_title(CaseData)
CaseData = eval(Template(str(CaseData)).render(self.all_var))
print(CaseData)
# 写Excle的行和列
row = CaseData["id"]
column = 11
# 初始化对应的值:
res = None
msg = None
value = None
# -------------------------发送请求-------------------------------
try:
# 请求数据
url = CaseData["url"] + CaseData["path"]
params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else None
headers = eval(CaseData["headers"]) if isinstance(CaseData["headers"], str) else None
if CaseData["data"] is None: # 会影响框架的性能
data = None
else:
# 进行加密处理
data = FileReader.data_EncryptDateAES(eval(CaseData["data"]))
dict_data = {
"url": url,
"params": params,
"headers": headers,
"data": data
}
if CaseData["type"] == "json":
dict_data["data"] = json.dumps(dict_data["data"])
except Exception:
value = MSG_DATA_ERROR
FileReader.writeDataToExcel(row=row, column=column, value=value)
else: # 响应对象
# 得到对应的响应数据
res = getattr(self.ak, CaseData["method"])(**dict_data)
# -------------------------提取数据库的操作---------------------------
self.__sql_extraction(CaseData)
# -------------------------进行断言处理-------------------------------
# 实际结果
try:
msg = self.ak.get_text(res.json(), CaseData["actualResult"])
except Exception:
value = MSG_EXDATA_ERROR
FileReader.writeDataToExcel(row=row, column=column, value=value)
else:
# 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
if msg == CaseData["expectResult"]:
value = MSG_ASSERT_OK
# 成功之后进行数据提取
self.__json_extraction(CaseData, res)
else:
value = MSG_ASSERT_NO
FileReader.writeDataToExcel(row=row, column=column, value=value)
finally:
> assert msg == CaseData["expectResult"], value
E AssertionError: 测试失败,断言失败。
E assert '账号已存在' == '注册成功'
E - 注册成功
E + 账号已存在
testcase\Test_Excel_08.py:181: AssertionError conftest.py pytest_runtest_makereport 49
2023-11-15 21:26:11 INFO 异常:<ExceptionInfo AssertionError("测试失败,断言失败。\nassert '账号已存在' == '注册成功'\n - 注册成功\n + 账号已存在") tblen=16> conftest.py pytest_runtest_makereport 50
2023-11-15 21:26:11 INFO 用例耗时:0.17476039999999848 conftest.py pytest_runtest_makereport 51
2023-11-15 21:26:11 INFO ************************************** conftest.py pytest_runtest_makereport 52
2023-11-15 21:26:14 INFO 用例ID:testcase/Test_Excel_08.py::TestCase::testCase[CaseData0] conftest.py pytest_runtest_makereport 47
2023-11-15 21:26:14 INFO 测试结果:failed conftest.py pytest_runtest_makereport 48
2023-11-15 21:26:14 INFO 故障表示:self = <Test_Excel_08.TestCase object at 0x000001D249B9B9A0>
CaseData = {'actualResult': '$.msg', 'caseName': '注册成功', 'data': '{"accounts":"hami","pwd":"123456","type": "username"}', 'expectResult': '注册成功', ...}
@pytest.mark.flaky(reruns=3, reruns_delay=3)
@pytest.mark.parametrize("CaseData", AllCaseData)
def testCase(self, CaseData):
self.__dynamic_title(CaseData)
CaseData = eval(Template(str(CaseData)).render(self.all_var))
print(CaseData)
# 写Excle的行和列
row = CaseData["id"]
column = 11
# 初始化对应的值:
res = None
msg = None
value = None
# -------------------------发送请求-------------------------------
try:
# 请求数据
url = CaseData["url"] + CaseData["path"]
params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else None
headers = eval(CaseData["headers"]) if isinstance(CaseData["headers"], str) else None
if CaseData["data"] is None: # 会影响框架的性能
data = None
else:
# 进行加密处理
data = FileReader.data_EncryptDateAES(eval(CaseData["data"]))
dict_data = {
"url": url,
"params": params,
"headers": headers,
"data": data
}
if CaseData["type"] == "json":
dict_data["data"] = json.dumps(dict_data["data"])
except Exception:
value = MSG_DATA_ERROR
FileReader.writeDataToExcel(row=row, column=column, value=value)
else: # 响应对象
# 得到对应的响应数据
res = getattr(self.ak, CaseData["method"])(**dict_data)
# -------------------------提取数据库的操作---------------------------
self.__sql_extraction(CaseData)
# -------------------------进行断言处理-------------------------------
# 实际结果
try:
msg = self.ak.get_text(res.json(), CaseData["actualResult"])
except Exception:
value = MSG_EXDATA_ERROR
FileReader.writeDataToExcel(row=row, column=column, value=value)
else:
# 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
if msg == CaseData["expectResult"]:
value = MSG_ASSERT_OK
# 成功之后进行数据提取
self.__json_extraction(CaseData, res)
else:
value = MSG_ASSERT_NO
FileReader.writeDataToExcel(row=row, column=column, value=value)
finally:
> assert msg == CaseData["expectResult"], value
E AssertionError: 测试失败,断言失败。
E assert '账号已存在' == '注册成功'
E - 注册成功
E + 账号已存在
testcase\Test_Excel_08.py:181: AssertionError conftest.py pytest_runtest_makereport 49
2023-11-15 21:26:14 INFO 异常:<ExceptionInfo AssertionError("测试失败,断言失败。\nassert '账号已存在' == '注册成功'\n - 注册成功\n + 账号已存在") tblen=16> conftest.py pytest_runtest_makereport 50
2023-11-15 21:26:14 INFO 用例耗时:0.2534118999999997 conftest.py pytest_runtest_makereport 51
2023-11-15 21:26:14 INFO ************************************** conftest.py pytest_runtest_makereport 52
report_allure
testcase
Test_Ecel.py
# -*- coding: utf-8 -*-
# @Time : 2023/11/8 20:55
# @Author : Hami
import json
import pytest
from day09.P02_ApiFrameProject.common.FileDataDriver import FileReader
from day09.P02_ApiFrameProject.api_keyword.api_key import ApiKey
from day09.P02_ApiFrameProject.config import *
import allure
from jinja2 import Template # 变量渲染
class TestCase:
# 获取对应的数据 CaseData 需要从文档当中去进行读取
# 1. 获取数据(四要素) 2. 发送请求 3.获取响应数据 4.断言
AllCaseData = FileReader.read_excel()
ak = ApiKey()
# 定义:all_val 存放提取出的数据
all_var = {}
def __dynamic_title(self, CaseData):
# # 动态生成标题
# allure.dynamic.title(data[11])
# 如果存在自定义标题
if CaseData["caseName"] is not None:
# 动态生成标题
# allure.dynamic.title(CaseData["caseName"])
caseName = "CASEID:{}--{}".format(CaseData["id"], CaseData["caseName"])
allure.dynamic.title(caseName)
if CaseData["storyName"] is not None:
# 动态获取story模块名
allure.dynamic.story(CaseData["storyName"])
if CaseData["featureName"] is not None:
# 动态获取feature模块名
allure.dynamic.feature(CaseData["featureName"])
if CaseData["remark"] is not None:
# 动态获取备注信息
allure.dynamic.description(CaseData["remark"])
if CaseData["rank"] is not None:
# 动态获取级别信息(blocker、critical、normal、minor、trivial)
allure.dynamic.severity(CaseData["rank"])
def __json_extraction(self, CaseData, res):
"""
提取响应之后的数据
:param CaseData: 当前的Case,主要获取需要提取数据的字段:jsonExData
:param res:响应得到的对应的结果
:return:
"""
try:
if CaseData["jsonExData"]:
Exdata = eval(CaseData["jsonExData"]) # {"VAR_TOKEN":"$..token","MSG":"$.msg"}
print("需要提取的数据:>>>", Exdata)
for key, value in Exdata.items():
# 通过对应的jsonpath获取具体的数据
new_value = self.ak.get_text(res.json(), value)
self.all_var.update(
{key: new_value}
)
print("提取出来的数据:>>>", self.all_var)
else:
print("需要提取的数据为空")
except Exception:
print("请检查你需要提取数据数据格式的正确性。")
def __sql_extraction(self, CaseData):
"""
从数据库提取数据
:param CaseData: 当前的Case,主要获取需要提取数据的字段:sqlExData
:return:
"""
try:
if CaseData["sqlExData"]:
Exdata = eval(CaseData[
"sqlExData"]) # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}
print("SQL-需要提取的数据:>>>", Exdata)
for key, value in Exdata.items():
# 通过对应的sql获取具体的数据
new_value = self.ak.get_sqlData(value)
self.all_var.update(
{key: new_value}
)
print("SQL-提取出来的数据:>>>", self.all_var)
else:
print("SQL-需要提取的数据为空")
except Exception:
print("SQL-请检查你需要提取数据数据格式的正确性。")
def __sql_assertData(self, CaseData):
res = True
if CaseData["sqlAssertData"] and CaseData["sqlExpectResult"]:
# 实际结果:从数据库读取出来的数据--字典的格式 # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}
realityData = eval(CaseData["sqlAssertData"])
# 期望结果:{"name":"hami","id":75}
expectData = json.loads(CaseData["sqlExpectResult"])
realityDataDict = {}
for key, value in realityData.items():
# 通过对应的sql获取具体的数据
new_value = self.ak.get_sqlData(value)
realityDataDict.update(
{key: new_value}
)
if expectData != realityDataDict:
res = False
return res
@pytest.mark.flaky(reruns=3, reruns_delay=3)
@pytest.mark.parametrize("CaseData", AllCaseData)
def testCase(self, CaseData):
self.__dynamic_title(CaseData)
CaseData = eval(Template(str(CaseData)).render(self.all_var))
print(CaseData)
# 写Excle的行和列
row = CaseData["id"]
column = 11
# 初始化对应的值:
res = None
msg = None
value = None
# -------------------------发送请求-------------------------------
try:
# 请求数据
url = CaseData["url"] + CaseData["path"]
params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else None
headers = eval(CaseData["headers"]) if isinstance(CaseData["headers"], str) else None
if CaseData["data"] is None: # 会影响框架的性能
data = None
else:
# 进行加密处理
data = FileReader.data_EncryptDateAES(eval(CaseData["data"]))
dict_data = {
"url": url,
"params": params,
"headers": headers,
"data": data
}
if CaseData["type"] == "json":
dict_data["data"] = json.dumps(dict_data["data"])
except Exception:
value = MSG_DATA_ERROR
FileReader.writeDataToExcel(row=row, column=column, value=value)
else: # 响应对象
# 得到对应的响应数据
res = getattr(self.ak, CaseData["method"])(**dict_data)
# -------------------------提取数据库的操作---------------------------
self.__sql_extraction(CaseData)
# -------------------------进行断言处理-------------------------------
# 实际结果
try:
msg = self.ak.get_text(res.json(), CaseData["actualResult"])
except Exception:
value = MSG_EXDATA_ERROR
FileReader.writeDataToExcel(row=row, column=column, value=value)
else:
# 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
if msg == CaseData["expectResult"]:
value = MSG_ASSERT_OK
# 成功之后进行数据提取
self.__json_extraction(CaseData, res)
else:
value = MSG_ASSERT_NO
FileReader.writeDataToExcel(row=row, column=column, value=value)
finally:
assert msg == CaseData["expectResult"], value
# -------------------------进行数据库断言处理-------------------------------
try:
sqlAssertRes = self.__sql_assertData(CaseData) # False True
except:
print("SQL断言出现问题")
value = "SQL断言出现问题"
sqlAssertRes = False
assert sqlAssertRes, value
else:
assert sqlAssertRes
finally:
FileReader.writeDataToExcel(row=row, column=column, value=value)
# -------------------------响应文本全量断言-------------------------------
if CaseData["responseExpect"]:
# 期望数据
json1 = eval(CaseData["responseExpect"])
other = eval(CaseData["responseExclude"])
jsonMaxDataRes = self.ak.jsonDeepDiff(json1, res.json(), **other)
assert jsonMaxDataRes, "两者不一致"
Test_runner.py
# -*- coding: utf-8 -*-
# @Time : 2023/11/8 20:55
# @Author : Hami
import json
import pytest
from day09.P02_ApiFrameProject.common.FileDataDriver import FileReader
from day09.P02_ApiFrameProject.api_keyword.api_key import ApiKey
from day09.P02_ApiFrameProject.config import *
import allure
from jinja2 import Template # 变量渲染
class TestCase:
# 获取对应的数据 CaseData 需要从文档当中去进行读取
# 1. 获取数据(四要素) 2. 发送请求 3.获取响应数据 4.断言
AllCaseData = FileReader.read_yaml()
ak = ApiKey()
# 定义:all_val 存放提取出的数据
all_var = {}
def __dynamic_title(self, CaseData):
# # 动态生成标题
# allure.dynamic.title(data[11])
# 如果存在自定义标题
if CaseData["caseName"] is not None:
# 动态生成标题
allure.dynamic.title(CaseData["caseName"])
if CaseData["storyName"] is not None:
# 动态获取story模块名
allure.dynamic.story(CaseData["storyName"])
if CaseData["featureName"] is not None:
# 动态获取feature模块名
allure.dynamic.feature(CaseData["featureName"])
if CaseData["remark"] is not None:
# 动态获取备注信息
allure.dynamic.description(CaseData["remark"])
if CaseData["rank"] is not None:
# 动态获取级别信息(blocker、critical、normal、minor、trivial)
allure.dynamic.severity(CaseData["rank"])
def __json_extraction(self, CaseData, res):
"""
提取响应之后的数据
:param CaseData: 当前的Case,主要获取需要提取数据的字段:jsonExData
:param res:响应得到的对应的结果
:return:
"""
try:
if CaseData["jsonExData"]:
Exdata = CaseData["jsonExData"] # {"VAR_TOKEN":"$..token","MSG":"$.msg"}
print("需要提取的数据:>>>", Exdata)
for key, value in Exdata.items():
# 通过对应的jsonpath获取具体的数据
new_value = self.ak.get_text(res.json(), value)
self.all_var.update(
{key: new_value}
)
print("提取出来的数据:>>>", self.all_var)
else:
print("需要提取的数据为空")
except Exception:
print("请检查你需要提取数据数据格式的正确性。")
def __sql_extraction(self, CaseData):
"""
从数据库提取数据
:param CaseData: 当前的Case,主要获取需要提取数据的字段:sqlExData
:return:
"""
try:
if CaseData["sqlExData"]:
Exdata = CaseData["sqlExData"] # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}
print("SQL-需要提取的数据:>>>", Exdata)
for key, value in Exdata.items():
# 通过对应的sql获取具体的数据
new_value = self.ak.get_sqlData(value)
self.all_var.update(
{key: new_value}
)
print("SQL-提取出来的数据:>>>", self.all_var)
else:
print("SQL-需要提取的数据为空")
except Exception:
print("SQL-请检查你需要提取数据数据格式的正确性。")
def __sql_assertData(self, CaseData):
res = True
if CaseData["sqlAssertData"] and CaseData["sqlExpectResult"]:
# 实际结果:从数据库读取出来的数据--字典的格式 # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}
realityData = CaseData["sqlAssertData"]
# 期望结果:{"name":"hami","id":75}
expectData = CaseData["sqlExpectResult"]
realityDataDict = {}
for key, value in realityData.items():
# 通过对应的sql获取具体的数据
new_value = self.ak.get_sqlData(value)
realityDataDict.update(
{key: new_value}
)
if expectData != realityDataDict:
res = False
return res
@pytest.mark.parametrize("CaseData", AllCaseData)
def testCase(self, CaseData):
self.__dynamic_title(CaseData)
CaseData = eval(Template(str(CaseData)).render(self.all_var))
print(CaseData)
# 写Yaml的下标
row = CaseData["id"]
# 初始化对应的值:
res = None
msg = None
value = None
# 知识:是否断言
# is_assert = True
# -------------------------发送请求-------------------------------
try:
# 请求数据
params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else CaseData["params"]
dict_data = {
"url": CaseData["url"] + CaseData["path"],
"params": params,
"headers": CaseData["headers"],
"data": CaseData["data"]
}
if CaseData["type"] == "json":
dict_data["data"] = json.dumps(dict_data["data"])
except Exception:
value = MSG_DATA_ERROR
CaseData["result"] = value
else:
# 得到对应的响应数据
res = getattr(self.ak, CaseData["method"])(**dict_data)
# -------------------------提取数据库的操作---------------------------
self.__sql_extraction(CaseData)
# -------------------------进行断言处理-------------------------------
# 实际结果
try:
msg = self.ak.get_text(res.json(), CaseData["actualResult"])
except Exception:
value = MSG_EXDATA_ERROR
CaseData["result"] = value
else:
# 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
if msg == CaseData["expectResult"]:
value = MSG_ASSERT_OK
# 成功之后进行数据提取
self.__json_extraction(CaseData, res)
else:
# is_assert = False
value = MSG_ASSERT_NO
CaseData["result"] = value
finally:
assert msg == CaseData["expectResult"], value
# -------------------------进行数据库断言处理-------------------------------
try:
res = self.__sql_assertData(CaseData) # False True
except:
print("SQL断言出现问题")
value = "SQL断言出现问题"
assert res, value
else:
assert res
finally:
CaseData["result"] = value
# -------------------------把当前的CaseData值写入:可以通过后置处理方法去处理-------------------------------
self.AllCaseData[row] = CaseData
FileReader.write_yaml(self.AllCaseData)
config.py
# -*- coding: utf-8 -*-
# @Time : 2023/11/5 22:24
# @Author : Hami
# 配置常量的文件
# 环境变量
# 正式环境
PROJECT_URL = "http://shop-xo.hctestedu.com/index.php"
# 测试环境
# PROJECT_URL = "http://127.0.0.1:8080"
# # 运行模式:
# RUNMODE = "EXCEL"
# RUNMODE = "YAML"
# 测试用例的路径:
# CASEDATAURL = "./data/excel/api_cases_V1.xlsx"
# CASEDATAURL = "./data/excel/api_cases_V2.xlsx"
# CASEDATAURL = "./data/excel/api_cases_V3.xlsx"
# CASEDATAURL = "./data/excel/api_cases_V4.xlsx"
# CASEDATAURL = "./data/excel/api_cases_V5.xlsx"
# CASEDATAURL = "./data/excel/api_cases_V7.xlsx"
CASEDATAURL = "./data/excel/api_cases_V7_error.xlsx"
SHEETNAME = "Sheet1"
YAMLDATA = "./data/yaml/api_yaml_V6.yaml"
# 测试账号
USERNAME = "hami"
PASSWORD = "123456"
LOGINTYPE = "username"
PULIC_DATA = {"application": "app", "application_client_type": "weixin"}
# 错误提示异常信息
MSG_DATA_ERROR = "数据解析错误,请检查dict_data的值的正确性。"
MSG_EXDATA_ERROR = "响应数据提取失败。"
MSG_ASSERT_OK = "测试通过,断言成功。"
MSG_ASSERT_NO = "测试失败,断言失败。"
# 测试数据库的连接信息
DB_HOST = 'shop-xo.hctestedu.com' # 数据库地址
DB_PORT = 3306
DB_USER = 'api_test' # 数据库用户名
DB_PASSWORD = 'Aa9999!' # 数据库密码
DB_NAME = 'shopxo_hctested' # 数据库名称
conftest.py
# -*- coding: utf-8 -*-
# @Time : 2023/11/5 22:09
# @Author : Hami
import pytest
import logging
from day09.P02_ApiFrameProject.api_keyword.api_key import ApiKey
# 注意:名字是固定的。
# 可以写具体的方法、正常的调用某个接口
# pytest在运行的时候【会自动】先调起这个文件,后面还具体调用这个方法
# 方法:获取token
# 1. 正常的请求对应的接口并且提取数据
# 2. 告诉别人这个是一个测试夹具(测试前置、后置操作):@pytest.fixture()
from day09.P02_ApiFrameProject.config import *
@pytest.fixture(scope= "session")
def token_fix():
"""
代表我们这个夹具运行一次
:return:
"""
print("开始运行:token_fix")
print("USERNAME:",USERNAME)
# 1. 实例化对象:ApiKey
ak = ApiKey()
# 2. 通过对应的类调用对应的方法 --四要素
# url = "http://shop-xo.hctestedu.com/index.php?s=/api/user/login"
url = PROJECT_URL+"?s=/api/user/login"
pulic_data = PULIC_DATA
data = {"accounts": USERNAME, "pwd": PASSWORD, "type": LOGINTYPE}
# 3. 发送请求
res = ak.post(url=url, params=pulic_data, data=data)
# 4. 提取数据
token = ak.get_text(res.json(), "$..token")
# 5. 返回数据
return ak,token
# 当执行一个case的时候会自动的调用这个方法:把对应的数据传过来给到call
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
# 通过 out = yield 定义了一个生成器。在生成器中,res = out.get_result() 获取了测试结果对象。
out = yield
res = out.get_result()
# res.when == "call":表示正在运行调用测试函数的阶段。
if res.when == "call":
logging.info(f"用例ID:{res.nodeid}")
logging.info(f"测试结果:{res.outcome}")
logging.info(f"故障表示:{res.longrepr}")
logging.info(f"异常:{call.excinfo}")
logging.info(f"用例耗时:{res.duration}")
logging.info("**************************************")
main.run.py
# -*- coding: utf-8 -*-
# @Time : 2023/11/5 21:09
# @Author : Hami
# https://pypi.tuna.tsinghua.edu.cn/simple 清华镜像
import os
# 框架的运行入口
import pytest
if __name__ == '__main__':
# 通过pytest 运行 并且生成allure报告【默认】
# 第一步: 指定运行文件,通过文件会生成运行结果后的数据放在:./result ; --clean-alluredir(每次运行之前清空这个文件夹的历史数据)
pytest.main(["-vs", "./testcase/Test_Excel_08.py", "--alluredir", "./result", "--clean-alluredir"])
# 第二步:把数据转成测试报告(html):allure generate ./result -o ./report_allure --clean
# os.system() # 在cmd(终端)去运行命令
os.system("allure generate ./result -o ./report_allure --clean")
pytest.ini
[pytest]
log_cli= false
log_level=NOTSET
log_format = %(asctime)s %(levelname)s %(message)s %(filename)s %(funcName)s %(lineno)d
log_date_format = %Y-%m-%d %H:%M:%S
log_file = ./logdata/log.log
log_file_level = info
log_file_format = %(asctime)s %(levelname)s %(message)s %(filename)s %(funcName)s %(lineno)d
log_file_date_format = %Y-%m-%d %H:%M:%S