可以做推廣的門戶網(wǎng)站軟件開發(fā)工資一般多少
1.pytest數(shù)據(jù)參數(shù)化
????????假設(shè)你需要測(cè)試一個(gè)登錄功能,輸入用戶名和密碼后驗(yàn)證登錄結(jié)果??梢允褂脜?shù)化實(shí)現(xiàn)多組輸入數(shù)據(jù)的測(cè)試:
????????測(cè)試正確的用戶名和密碼登錄成功
????????測(cè)試正確的用戶名和錯(cuò)誤的密碼登錄失敗
????????測(cè)試錯(cuò)誤的用戶名和正確的密碼登錄失敗
????????測(cè)試錯(cuò)誤的用戶名和密碼登錄失敗
????????在參數(shù)化中我們可以單參數(shù)、多參數(shù)、函數(shù)數(shù)據(jù)參數(shù)化
????????不管哪一種場景,它們都是數(shù)據(jù)不一樣而產(chǎn)生的問題。核心步驟其實(shí)都是一樣的---發(fā)送請(qǐng)求。
? ? ? ? 我們采取的思想就是進(jìn)行數(shù)據(jù)分離--DDT數(shù)據(jù)驅(qū)動(dòng)
????????
# 1. 第一個(gè)情況:單數(shù)據(jù)
data = [值1,值2,值3...]
data = (值1,值2,值3...)單數(shù)據(jù),通過對(duì)應(yīng)的下標(biāo)去進(jìn)行獲取【pytest會(huì)自己進(jìn)行處理】# 2. 第一個(gè)情況:多數(shù)據(jù),列表和元組嵌套
data = [("admin","123456"),("admin","123456"),("admin","123456"),...]
data = (["admin","123456"],["admin","123456"],...)
多數(shù)據(jù),通過對(duì)應(yīng)的下標(biāo)去進(jìn)行獲取# 3.【重要】:列表嵌套字典
data = [{“name”:"admin","password":"123456"},
{“name”:"admin","password":"123456"},
{“name”:"admin","password":"123456"}
...]
多數(shù)據(jù),通過對(duì)應(yīng)的KEY去進(jìn)行獲取2. 引用對(duì)應(yīng)的數(shù)據(jù)
在對(duì)應(yīng)的方法上去加上一個(gè)裝飾器即可引用:
@pytest.mark.parametrize(argnames,argvalues)
@pytest.mark.parametrize("變量名",引用的變量的值) # 引用上面的數(shù)據(jù),并且取名加:變量名
? ? ? ?2. Pytest+Excel接口自動(dòng)化框架
? ? ? ? 我們?nèi)绾螌?duì)應(yīng)的數(shù)據(jù)信息進(jìn)行提取出來,我們?nèi)绾问褂胮ython來讀取Excel
讀取excel的方法如下
????????
import openpyxl
from collectionsFramework.P02_pytest_excel_allure.config import *class FileReader:"""專門用來讀取和寫入yaml、excel文件"""# 讀取excel--openpyxl -- 文件格式:.xlsx@staticmethod # 直接通過類名進(jìn)行調(diào)用def read_excel(file_path=CASEDATAURL, sheet_name=SHEETNAME):"""讀取Excel文件,只支持 .xlsx文件:param file_path: 文件路徑:return: excel文件數(shù)據(jù),元組的格式"""# 打開現(xiàn)有的Excel文件或創(chuàng)建新的文件try:# 正常情況下直接打開workbook = openpyxl.load_workbook(file_path)except FileNotFoundError:workbook = openpyxl.Workbook()# 選擇或創(chuàng)建指定的工作表if sheet_name in workbook.sheetnames:# 【正常】 判斷有沒有對(duì)應(yīng)的shtttname ,有的話把對(duì)應(yīng)的數(shù)據(jù)給我加載出來worksheet = workbook[sheet_name]else:# 沒有的話,給我新建一個(gè)worksheet = workbook.create_sheet(sheet_name)# 獲取列名 --- 把第2行的數(shù)據(jù)拿出來作為我們的key值headers = [cell.value for cell in worksheet[2]]# print("所有的key", headers)# 將數(shù)據(jù)存儲(chǔ)為字典,并且放在我們data當(dāng)中data = [] # 所有的數(shù)據(jù)# 把小的數(shù)據(jù)從第三行開始for row in worksheet.iter_rows(min_row=3, values_only=True):data.append(dict(zip(headers, row)))# data.append(dict(zip(headers, row)))# data.append()# dict(zip(headers, row)) # key 和 value 一一對(duì)應(yīng)起來 ==={}workbook.close()# 所有的數(shù)據(jù)return dataif __name__ == '__main__':CaseData = FileReader.read_excel()print(CaseData)
EXCEL讀取的方法封裝
????????
????????
# -*- coding: utf-8 -*-
# @Time : 2023/11/8 20:55
# @Author : Hami
import json
import pytest
from collectionsFramework.P02_pytest_excel_allure.common.FileDataDriver import FileReader
from collectionsFramework.P02_pytest_excel_allure.api_keyword.api_key import ApiKeyclass TestCase:# 獲取對(duì)應(yīng)的數(shù)據(jù) CaseData 需要從文檔當(dāng)中去進(jìn)行讀取CaseData = FileReader.read_excel()ak = ApiKey()@pytest.mark.parametrize("CaseData", CaseData)def testCase(self, CaseData):# 沒一個(gè)對(duì)應(yīng)的數(shù)據(jù)都來臨,獲取對(duì)應(yīng)的接口請(qǐng)求的四要素# 1. 獲取數(shù)據(jù)(四要素) 2. 發(fā)送請(qǐng)求 3.獲取響應(yīng)數(shù)據(jù) 4.斷言# 1. 字典獲取的方式依次拿到# url = CaseData["url"]+CaseData["path"]# params = eval(CaseData["params"])# headers = eval(CaseData["headers"])# data = eval(CaseData["data"])dict_data = {"url":CaseData["url"]+CaseData["path"],"params": eval(CaseData["params"]),"headers": eval(CaseData["headers"]),"data": eval(CaseData["data"])}if CaseData["type"] == "json":dict_data["data"] = json.dumps(dict_data["data"])# self.ak.post(url=url,....) # 不建議res = self.ak.post(**dict_data) # 不定長參數(shù)傳值方式print(res)
eval
eval() 函數(shù)是 Python 內(nèi)置的一個(gè)函數(shù),用于將字符串作為代碼執(zhí)行,并返回結(jié)果。
?
def add(a, b):
return a + b
result = eval("add(2, 3)")
print(result) # 輸出: 5
getattr()函數(shù)的應(yīng)用
????????getattr() 函數(shù)是 Python 內(nèi)置的一個(gè)函數(shù),用于獲取對(duì)象的屬性或方法
? ? getattr(object, name, default)
?object : 表示要獲取屬性或方法的對(duì)象。
name : 表示要獲取的屬性或方法的名稱。
default (可選): 表示當(dāng)屬性或方法不存在時(shí)的默認(rèn)值
例子如下
????????
"""
`getattr()` 函數(shù)是 Python 內(nèi)置的一個(gè)函數(shù),用于獲取對(duì)象的屬性或方法。
語法結(jié)構(gòu):
getattr(object, name, default)
- `object`: 表示要獲取屬性或方法的對(duì)象。
- `name`: 表示要獲取的屬性或方法的名稱。
- `default` (可選): 表示當(dāng)屬性或方法不存在時(shí)的默認(rèn)值。
"""
# 案例一:類當(dāng)中只有屬性
class MyClass:
name = "hami"
age = 18
obj = MyClass()
value = getattr(obj, "name")
print(value) # 輸出: hami
# 案例二:類當(dāng)中對(duì)應(yīng)的屬性不存在(有參數(shù)),如果直接參數(shù)不存在也沒有給參數(shù),報(bào)錯(cuò)
class MyClass:
name = "hami"
age = 18
obj = MyClass()
value = getattr(obj, "sex", "女")
print(value) # 輸出: hami
# 案例三:類當(dāng)中對(duì)應(yīng)的方法,一定要記得調(diào)用(無參數(shù))
class MyClass:
def greet(self):
print("Hello, world!")
obj = MyClass()
method = getattr(obj, "greet") # 返回對(duì)應(yīng)的方法
method() # 輸出: Hello, world!
# 案例四:類當(dāng)中對(duì)應(yīng)的方法,一定要記得調(diào)用(有參數(shù))
class MyClass:
def greet(self,name,age):
print("您的姓名是: {},年齡是:{}".format(name,age))
obj = MyClass()
method = getattr(obj, "greet")("hami","25") # 您的姓名是hami,年齡是:25
封裝主函數(shù)
????????
import json
import pytest
from collectionsFramework.P02_pytest_excel_allure.common.FileDataDriver import FileReader
from collectionsFramework.P02_pytest_excel_allure.api_keyword.api_key import ApiKey
from collectionsFramework.P02_pytest_excel_allure.config import *class TestCase:# 獲取對(duì)應(yīng)的數(shù)據(jù) CaseData 需要從文檔當(dāng)中去進(jìn)行讀取# 1. 獲取數(shù)據(jù)(四要素) 2. 發(fā)送請(qǐng)求 3.獲取響應(yīng)數(shù)據(jù) 4.斷言CaseData = FileReader.read_excel()ak = ApiKey()@pytest.mark.parametrize("CaseData", CaseData)def testCase(self, CaseData):# -------------------------發(fā)送請(qǐng)求-------------------------------try:# 請(qǐng)求數(shù)據(jù)dict_data = {"url": CaseData["url"] + CaseData["path"],"params": eval(CaseData["params"]),"headers": eval(CaseData["headers"]),"data": eval(CaseData["data"])}if CaseData["type"] == "json":dict_data["data"] = json.dumps(dict_data["data"])except Exception:value = MSG_DATA_ERROR# ----待定----print("寫入測(cè)試結(jié)果", value)else:# 得到對(duì)應(yīng)的響應(yīng)數(shù)據(jù)res = getattr(self.ak, CaseData["method"])(**dict_data)# -------------------------進(jìn)行斷言處理-------------------------------# 實(shí)際結(jié)果try:msg = self.ak.get_text(res.json(),CaseData["actualResult"])except Exception:value = MSG_EXDATA_ERROR# ----待定----print("寫入測(cè)試結(jié)果", value)else:# 只會(huì)是一個(gè)分支語言,但是不會(huì)造成測(cè)試結(jié)果成功或者失敗,所以必須無論如何都是需要斷言if msg == CaseData["expectResult"]:value = MSG_ASSERT_OKelse:value = MSG_ASSERT_NO# ----待定----print("寫入測(cè)試結(jié)果", value)finally:assert msg == CaseData["expectResult"],value
Allure報(bào)告日志及動(dòng)態(tài)標(biāo)題
????????
import json
import pytest
from collectionsFramework.P02_pytest_excel_allure.common.FileDataDriver import FileReader
from collectionsFramework.P02_pytest_excel_allure.api_keyword.api_key import ApiKey
from collectionsFramework.P02_pytest_excel_allure.config import *import allureclass TestCase:# 獲取對(duì)應(yīng)的數(shù)據(jù) CaseData 需要從文檔當(dāng)中去進(jìn)行讀取# 1. 獲取數(shù)據(jù)(四要素) 2. 發(fā)送請(qǐng)求 3.獲取響應(yīng)數(shù)據(jù) 4.斷言CaseData = FileReader.read_excel()ak = ApiKey()def __dynamic_title(self, CaseData):# # 動(dòng)態(tài)生成標(biāo)題# allure.dynamic.title(data[11])# 如果存在自定義標(biāo)題if CaseData["caseName"] is not None:# 動(dòng)態(tài)生成標(biāo)題allure.dynamic.title(CaseData["caseName"])if CaseData["storyName"] is not None:# 動(dòng)態(tài)獲取story模塊名allure.dynamic.story(CaseData["storyName"])if CaseData["featureName"] is not None:# 動(dòng)態(tài)獲取feature模塊名allure.dynamic.feature(CaseData["featureName"])if CaseData["remark"] is not None:# 動(dòng)態(tài)獲取備注信息allure.dynamic.description(CaseData["remark"])if CaseData["rank"] is not None:# 動(dòng)態(tài)獲取級(jí)別信息(blocker、critical、normal、minor、trivial)allure.dynamic.severity(CaseData["rank"])@pytest.mark.parametrize("CaseData", CaseData)def testCase(self, CaseData):self.__dynamic_title(CaseData)# -------------------------發(fā)送請(qǐng)求-------------------------------try:# 請(qǐng)求數(shù)據(jù)dict_data = {"url": CaseData["url"] + CaseData["path"],"params": eval(CaseData["params"]),"headers": eval(CaseData["headers"]),"data": eval(CaseData["data"])}if CaseData["type"] == "json":dict_data["data"] = json.dumps(dict_data["data"])except Exception:value = MSG_DATA_ERROR# ----待定----print("寫入測(cè)試結(jié)果", value)else:# 得到對(duì)應(yīng)的響應(yīng)數(shù)據(jù)res = getattr(self.ak, CaseData["method"])(**dict_data)# -------------------------進(jìn)行斷言處理-------------------------------# 實(shí)際結(jié)果try:msg = self.ak.get_text(res.json(),CaseData["actualResult"])except Exception:value = MSG_EXDATA_ERROR# ----待定----print("寫入測(cè)試結(jié)果", value)else:# 只會(huì)是一個(gè)分支語言,但是不會(huì)造成測(cè)試結(jié)果成功或者失敗,所以必須無論如何都是需要斷言if msg == CaseData["expectResult"]:value = MSG_ASSERT_OKelse:value = MSG_ASSERT_NO# ----待定----print("寫入測(cè)試結(jié)果", value)finally:assert msg == CaseData["expectResult"],value
3.Excel框架優(yōu)化及Yaml入門
? ? ? ? ?1.寫入excel框架
? ? ? ? ? ? ? ? ? ? ? ? 我們?cè)贔ileDataDriver.py, 增加一個(gè)寫入excel的方法
????????????????????????
@staticmethoddef writeDataToExcel(file_path=CASEDATAURL, sheet_name=SHEETNAME, row=None, column=None, value=None):# 打開現(xiàn)有的Excel文件或創(chuàng)建新的文件try:workbook = openpyxl.load_workbook(file_path)except FileNotFoundError:workbook = openpyxl.Workbook()# 選擇或創(chuàng)建指定的工作表if sheet_name in workbook.sheetnames:worksheet = workbook[sheet_name]else:worksheet = workbook.create_sheet(sheet_name)# 寫入數(shù)據(jù)到指定行和列worksheet.cell(row=row, column=column).value = value# 保存修改后的文件--- 所以執(zhí)行過程當(dāng)中excel是要關(guān)閉的狀態(tài)workbook.save(file_path)
? ? ? ? 在config.py中寫入對(duì)應(yīng)的常量,我們就可以在對(duì)應(yīng)的測(cè)試用例中修改代碼如下
????????
import json
import pytest
from YamlOptimization.P02_pytest_excel_allure.common.FileDataDriver import FileReader
from YamlOptimization.P02_pytest_excel_allure.api_keyword.api_key import ApiKey
from YamlOptimization.P02_pytest_excel_allure.config import *
import allureclass TestCase:# 獲取對(duì)應(yīng)的數(shù)據(jù) CaseData 需要從文檔當(dāng)中去進(jìn)行讀取# 1. 獲取數(shù)據(jù)(四要素) 2. 發(fā)送請(qǐng)求 3.獲取響應(yīng)數(shù)據(jù) 4.斷言AllCaseData = FileReader.read_excel()ak = ApiKey()def __dynamic_title(self, CaseData):# # 動(dòng)態(tài)生成標(biāo)題# allure.dynamic.title(data[11])# 如果存在自定義標(biāo)題if CaseData["caseName"] is not None:# 動(dòng)態(tài)生成標(biāo)題allure.dynamic.title(CaseData["caseName"])if CaseData["storyName"] is not None:# 動(dòng)態(tài)獲取story模塊名allure.dynamic.story(CaseData["storyName"])if CaseData["featureName"] is not None:# 動(dòng)態(tài)獲取feature模塊名allure.dynamic.feature(CaseData["featureName"])if CaseData["remark"] is not None:# 動(dòng)態(tài)獲取備注信息allure.dynamic.description(CaseData["remark"])if CaseData["rank"] is not None:# 動(dòng)態(tài)獲取級(jí)別信息(blocker、critical、normal、minor、trivial)allure.dynamic.severity(CaseData["rank"])@pytest.mark.parametrize("CaseData", AllCaseData)def testCase(self, CaseData):self.__dynamic_title(CaseData)# 寫Excle的行和列row = CaseData["id"]column = 11# 初始化對(duì)應(yīng)的值:res = Nonemsg = Nonevalue = None# -------------------------發(fā)送請(qǐng)求-------------------------------try:# 請(qǐng)求數(shù)據(jù)dict_data = {"url": CaseData["url"] + CaseData["path"],"params": eval(CaseData["params"]),"headers": eval(CaseData["headers"]),"data": eval(CaseData["data"])}if CaseData["type"] == "json":dict_data["data"] = json.dumps(dict_data["data"])except Exception:value = MSG_DATA_ERRORFileReader.writeDataToExcel(row=row,column=column,value=value)else:# 得到對(duì)應(yīng)的響應(yīng)數(shù)據(jù)res = getattr(self.ak, CaseData["method"])(**dict_data)# -------------------------進(jìn)行斷言處理-------------------------------# 實(shí)際結(jié)果try:msg = self.ak.get_text(res.json(), CaseData["actualResult"])except Exception:value = MSG_EXDATA_ERRORFileReader.writeDataToExcel(row=row,column=column,value=value)else:# 只會(huì)是一個(gè)分支語言,但是不會(huì)造成測(cè)試結(jié)果成功或者失敗,所以必須無論如何都是需要斷言if msg == CaseData["expectResult"]:value = MSG_ASSERT_OKelse:value = MSG_ASSERT_NOFileReader.writeDataToExcel(row=row,column=column,value=value)finally:assert msg == CaseData["expectResult"], value
? ?? ? ?2.實(shí)現(xiàn)優(yōu)化接口關(guān)聯(lián)
? ? ? ? 思路:
- excel ---字段 {"key":"value"} ----{"變量名":"jsonpath值","變量名":"jsonpath值"}
{"token":"$..token","name":"$..name"}
? ? 2. 寫一個(gè)方法:
循環(huán)遍歷這個(gè)值 :{"token":"$..token","name":"$..name"}
遍歷的過程當(dāng)中,通過"$..token"提取具體的值:56465456456313521456 ---new_value
3. 定義一個(gè)全局變量:專門用來存放提取之后的數(shù)據(jù)。
all_val={"token":"56465456456313521456","name":"hami"}
代碼如下:
????????
def __json_extraction(self, CaseData, res):"""提取響應(yīng)之后的數(shù)據(jù):param CaseData: 當(dāng)前的Case,主要獲取需要提取數(shù)據(jù)的字段:jsonExData:param res:響應(yīng)得到的對(duì)應(yīng)的結(jié)果:return:"""try:if CaseData["jsonExData"]:Exdata = eval(CaseData["jsonExData"]) # {"VAR_TOKEN":"$..token","MSG":"$.msg"}print("需要提取的數(shù)據(jù):>>>", Exdata)for key, value in Exdata.items():# 通過對(duì)應(yīng)的jsonpath獲取具體的數(shù)據(jù)new_value = self.ak.get_text(res.json(), value)self.all_var.update({key: new_value})print("提取出來的數(shù)據(jù):>>>", self.all_var)else:print("需要提取的數(shù)據(jù)為空")except Exception:print("請(qǐng)檢查你需要提取數(shù)據(jù)數(shù)據(jù)格式的正確性。")
3.進(jìn)行變量渲染
? ? ? ? 我們提取出來的casedata如下所示
????????
我們可以看到此處的id和var_token沒有具體的值,我們需要把具體的值渲染進(jìn)去
????????
all_val = {"VAR_TOKEN": "134324324324", "id": "158"}CaseData = {"id": 3,"url": "http://novel.hctestedu.com/book/queryBookDetail/{{id}}",'params': '{"application": "app",\n"application_client_type": "weixin",\n"token": "{{VAR_TOKEN}}"}'}CaseData = eval(Template(str(CaseData)).render(all_val))
就用template來進(jìn)行渲染
3.數(shù)據(jù)庫操作(提取數(shù)據(jù))
? ? ? ? 我們的思路如下:
- 框架能夠連接數(shù)據(jù)庫
- 執(zhí)行SQL
- 把數(shù)據(jù)返回
思路:同json提取器
從數(shù)據(jù)庫提取數(shù)據(jù):Excle進(jìn)行維護(hù) ;思路:同json提取器
? ?1.游標(biāo)對(duì)象
????????在數(shù)據(jù)庫中,游標(biāo)是一個(gè)十分重要的概念。游標(biāo)提供了一種從表中檢索出的數(shù)據(jù)進(jìn)行操作的靈活手段,就本質(zhì)而言,游標(biāo)實(shí)際上是一種能從包括多條數(shù)據(jù)記錄的結(jié)果集中每次提取一條記錄的機(jī)制。游標(biāo)總是與一條SQL 選擇語句相關(guān)聯(lián)因?yàn)橛螛?biāo)由結(jié)果集(可以是零條,一條或由相關(guān)的選擇語句檢索出的多條記錄)和結(jié)果集中指向特定記錄的游標(biāo)位置組成。當(dāng)決定對(duì)結(jié)果進(jìn)行處理時(shí),必須聲明一個(gè)指向該結(jié)果的游標(biāo)
????????
? ? ? ? 常用的方法:
????????cursor(): 創(chuàng)建游標(biāo)對(duì)象
????????close(): 關(guān)閉游標(biāo)對(duì)象
????????fetchone(): 得到結(jié)果集的下一行
????????fetchmany([size = cursor.arraysize]):得到結(jié)果集的下幾行fetchall():得到結(jié)果集中剩下的所有行
????????excute(sql[,args]): 執(zhí)行一個(gè)數(shù)據(jù)庫查詢或命令executemany(sql,args):執(zhí)行多個(gè)數(shù)據(jù)庫查詢或命令
? ? ? 例子如下:
????????
"""
在測(cè)試過程中偶然需要從數(shù)據(jù)庫獲取數(shù)據(jù)進(jìn)行測(cè)試或者通過數(shù)據(jù)庫的數(shù)據(jù)進(jìn)行斷言,這時(shí)候既要連接到數(shù)據(jù)庫。python當(dāng)中利用PySQL進(jìn)行連接
安裝:pip install pymysql
"""
import pymysql# 1. 配置數(shù)據(jù)庫連接信息并連接
connection = pymysql.connect(host='shop-xo.hctestedu.com', # 數(shù)據(jù)庫地址port=3306,user='api_test', # 數(shù)據(jù)庫用戶名password='Aa9999!', # 數(shù)據(jù)庫密碼db='shopxo_hctested', # 數(shù)據(jù)庫名稱
)# 2. 創(chuàng)建游標(biāo)對(duì)象,使用它進(jìn)行操作---人
cursor = connection.cursor()# 3. SQL語句---飯
sql = "SELECT username FROM sxo_user WHERE id = 75;"# 4. 使用游標(biāo)對(duì)象去執(zhí)行操作SQL
cursor.execute(sql)# 5. 得到結(jié)果集的下一行
result = cursor.fetchone()print(result) # 元組
# 6. 關(guān)閉數(shù)據(jù)庫連接
cursor.close()
2.我們繼續(xù)優(yōu)化代碼?將數(shù)據(jù)庫提取的操作寫入api_key和測(cè)試用例當(dāng)中
? ? ? ? api中
@allure.step(">>>>>>:開始提取數(shù)據(jù)庫的數(shù)據(jù)")def get_sqlData(self, sqlValue):""":param sqlValue: SQL,返回的數(shù)據(jù)是一個(gè)元組:return:"""import pymysql# 1. 配置數(shù)據(jù)庫連接信息并連接connection = pymysql.connect(host=DB_HOST, # 數(shù)據(jù)庫地址port=DB_PORT,user=DB_USER, # 數(shù)據(jù)庫用戶名password=DB_PASSWORD, # 數(shù)據(jù)庫密碼db=DB_NAME, # 數(shù)據(jù)庫名稱)# 2. 創(chuàng)建游標(biāo)對(duì)象,使用它進(jìn)行操作cursor = connection.cursor()# 4. 使用游標(biāo)對(duì)象去執(zhí)行操作SQLcursor.execute(sqlValue)# 5. 得到結(jié)果集的下一行result = cursor.fetchone()# 6. 關(guān)閉數(shù)據(jù)庫連接cursor.close()return result[0]
測(cè)試用例中
????????
def __sql_extraction(self,CaseData):"""從數(shù)據(jù)庫提取數(shù)據(jù):param CaseData: 當(dāng)前的Case,主要獲取需要提取數(shù)據(jù)的字段: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-需要提取的數(shù)據(jù):>>>", Exdata)for key, value in Exdata.items():# 通過對(duì)應(yīng)的sql獲取具體的數(shù)據(jù)new_value = self.ak.get_sqlData(value)self.all_var.update({key: new_value})print("SQL-提取出來的數(shù)據(jù):>>>", self.all_var)else:print("SQL-需要提取的數(shù)據(jù)為空")except Exception:print("SQL-請(qǐng)檢查你需要提取數(shù)據(jù)數(shù)據(jù)格式的正確性。")
4.數(shù)據(jù)庫操作-數(shù)據(jù)庫斷言【一般是極為重要的接口我們?nèi)プ觥?/h3>
- 檢查數(shù)據(jù)庫是否有這個(gè)用戶? --- 數(shù)據(jù)庫斷言
- 是否能夠正確的登錄
????????
思路:excel
- 期望結(jié)果:{"username":"yeye"}
- 實(shí)際結(jié)果:{"username":"SELECT username FROM sxo_user WHERE id=75"}
? ? ? ? 代碼如下
????????
def __sql_assertData(self, CaseData):res = Trueif CaseData["sqlAssertData"] and CaseData["sqlExpectRe ult"]:# 實(shí)際結(jié)果:從數(shù)據(jù)庫讀取出來的數(shù)據(jù)--字典的格式 # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}realityData = eval(CaseData["sqlAssertData"])# 期望結(jié)果:{"name":"hami","id":75}expectData = json.loads(CaseData["sqlExpectResult"])realityDataDict = {}for key, value in realityData.items():# 通過對(duì)應(yīng)的sql獲取具體的數(shù)據(jù)new_value = self.ak.get_sqlData(value)realityDataDict.update({key: new_value})if expectData != realityDataDict:res = Falsereturn res
整個(gè)側(cè)事故用例代碼如下
????????
# -*- coding: utf-8 -*-
# @Time : 2023/11/8 20:55
# @Author : Hami
import json
import pytest
from YamlOptimization.P02_pytest_excel_allure.common.FileDataDriver import FileReader
from YamlOptimization.P02_pytest_excel_allure.api_keyword.api_key import ApiKey
from YamlOptimization.P02_pytest_excel_allure.config import *
import allure
from jinja2 import Template # 變量渲染class TestCase:# 獲取對(duì)應(yīng)的數(shù)據(jù) CaseData 需要從文檔當(dāng)中去進(jìn)行讀取# 1. 獲取數(shù)據(jù)(四要素) 2. 發(fā)送請(qǐng)求 3.獲取響應(yīng)數(shù)據(jù) 4.斷言AllCaseData = FileReader.read_excel()ak = ApiKey()# 定義:all_val 存放提取出的數(shù)據(jù)all_var = {}def __dynamic_title(self, CaseData):# # 動(dòng)態(tài)生成標(biāo)題# allure.dynamic.title(data[11])# 如果存在自定義標(biāo)題if CaseData["caseName"] is not None:# 動(dòng)態(tài)生成標(biāo)題allure.dynamic.title(CaseData["caseName"])if CaseData["storyName"] is not None:# 動(dòng)態(tài)獲取story模塊名allure.dynamic.story(CaseData["storyName"])if CaseData["featureName"] is not None:# 動(dòng)態(tài)獲取feature模塊名allure.dynamic.feature(CaseData["featureName"])if CaseData["remark"] is not None:# 動(dòng)態(tài)獲取備注信息allure.dynamic.description(CaseData["remark"])if CaseData["rank"] is not None:# 動(dòng)態(tài)獲取級(jí)別信息(blocker、critical、normal、minor、trivial)allure.dynamic.severity(CaseData["rank"])def __json_extraction(self, CaseData, res):"""提取響應(yīng)之后的數(shù)據(jù):param CaseData: 當(dāng)前的Case,主要獲取需要提取數(shù)據(jù)的字段:jsonExData:param res:響應(yīng)得到的對(duì)應(yīng)的結(jié)果:return:"""try:if CaseData["jsonExData"]:Exdata = eval(CaseData["jsonExData"]) # {"VAR_TOKEN":"$..token","MSG":"$.msg"}print("需要提取的數(shù)據(jù):>>>", Exdata)for key, value in Exdata.items():# 通過對(duì)應(yīng)的jsonpath獲取具體的數(shù)據(jù)new_value = self.ak.get_text(res.json(), value)self.all_var.update({key: new_value})print("提取出來的數(shù)據(jù):>>>", self.all_var)else:print("需要提取的數(shù)據(jù)為空")except Exception:print("請(qǐng)檢查你需要提取數(shù)據(jù)數(shù)據(jù)格式的正確性。")def __sql_extraction(self, CaseData):"""從數(shù)據(jù)庫提取數(shù)據(jù):param CaseData: 當(dāng)前的Case,主要獲取需要提取數(shù)據(jù)的字段: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-需要提取的數(shù)據(jù):>>>", Exdata)for key, value in Exdata.items():# 通過對(duì)應(yīng)的sql獲取具體的數(shù)據(jù)new_value = self.ak.get_sqlData(value)self.all_var.update({key: new_value})print("SQL-提取出來的數(shù)據(jù):>>>", self.all_var)else:print("SQL-需要提取的數(shù)據(jù)為空")except Exception:print("SQL-請(qǐng)檢查你需要提取數(shù)據(jù)數(shù)據(jù)格式的正確性。")def __sql_assertData(self, CaseData):res = Trueif CaseData["sqlAssertData"] and CaseData["sqlExpectRe ult"]:# 實(shí)際結(jié)果:從數(shù)據(jù)庫讀取出來的數(shù)據(jù)--字典的格式 # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}realityData = eval(CaseData["sqlAssertData"])# 期望結(jié)果:{"name":"hami","id":75}expectData = json.loads(CaseData["sqlExpectResult"])realityDataDict = {}for key, value in realityData.items():# 通過對(duì)應(yīng)的sql獲取具體的數(shù)據(jù)new_value = self.ak.get_sqlData(value)realityDataDict.update({key: new_value})if expectData != realityDataDict:res = Falsereturn 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)# 寫Excle的行和列row = CaseData["id"]column = 11# 初始化對(duì)應(yīng)的值:res = Nonemsg = Nonevalue = None# -------------------------發(fā)送請(qǐng)求-------------------------------try:# 請(qǐng)求數(shù)據(jù)dict_data = {"url": CaseData["url"] + CaseData["path"],"params": eval(CaseData["params"]),"headers": eval(CaseData["headers"]),"data": eval(CaseData["data"])}if CaseData["type"] == "json":dict_data["data"] = json.dumps(dict_data["data"])except Exception:value = MSG_DATA_ERRORFileReader.writeDataToExcel(row=row, column=column, value=value)else:# 得到對(duì)應(yīng)的響應(yīng)數(shù)據(jù)res = getattr(self.ak, CaseData["method"])(**dict_data)# -------------------------提取數(shù)據(jù)庫的操作---------------------------self.__sql_extraction(CaseData)# -------------------------進(jìn)行斷言處理-------------------------------# 實(shí)際結(jié)果try:msg = self.ak.get_text(res.json(), CaseData["actualResult"])except Exception:value = MSG_EXDATA_ERRORFileReader.writeDataToExcel(row=row, column=column, value=value)else:# 只會(huì)是一個(gè)分支語言,但是不會(huì)造成測(cè)試結(jié)果成功或者失敗,所以必須無論如何都是需要斷言if msg == CaseData["expectResult"]:value = MSG_ASSERT_OK# 成功之后進(jìn)行數(shù)據(jù)提取self.__json_extraction(CaseData, res)else:value = MSG_ASSERT_NOFileReader.writeDataToExcel(row=row, column=column, value=value)finally:assert msg == CaseData["expectResult"], value# -------------------------進(jìn)行數(shù)據(jù)庫斷言處理-------------------------------try:res = self.__sql_assertData(CaseData) # False Trueexcept:print("SQL斷言出現(xiàn)問題")value = "SQL斷言出現(xiàn)問題"assert res, valueelse:assert resfinally:FileReader.writeDataToExcel(row=row, column=column, value=value)
5.yaml應(yīng)用?
? ? ? ? ? ? ? ? yaml的概念:
????????????????YAML 的語法和其他高級(jí)語言類似,并且可以簡單表達(dá)清單、散列表,標(biāo)量等數(shù)據(jù)形態(tài)。它使用空白符號(hào)縮進(jìn)和大量依賴外觀的特色,特別適合用來表達(dá)或編輯數(shù)據(jù)結(jié)構(gòu)、各種配置文件、傾印調(diào)試內(nèi)容、文件大綱(例如:許多電子郵件標(biāo)題格式和YAML非常接近)
????????YAML 的配置文件后綴為 .yml或.yaml,如:huace.yml
? ? ? ? 基本語法:
????????大小寫敏感
????????使用縮進(jìn)表示層級(jí)關(guān)系
????????縮進(jìn)不允許使用tab,只允許空格
????????縮進(jìn)的空格數(shù)不重要,只要相同層級(jí)的元素左對(duì)齊即可
????????'#' 表示注釋
? ? ? ? Yaml當(dāng)中如果是數(shù)字字符的話,一定要加雙引號(hào)(不加就是一個(gè)整型)
? ? ? ? 數(shù)據(jù)類型
純量:字符串/數(shù)字 ,直接寫就好了
列表:用符號(hào)去代替: -
字典:key : value
符號(hào)后面必須要有對(duì)應(yīng)空格
? ? ? ? ? ? ? ? 我們的yaml中可以這樣寫
????????????????
data:
-id: "0001"name: "qsdd"
-id: "0002"name: "yeyeyeye"
可以成功轉(zhuǎn)換為json格式
?也可以這樣寫:
data: [{ "id": "0001"},{ "id": "0002"}]
? ? ? ? 也可以轉(zhuǎn)換為json格式
? ? ? ? excel格式的轉(zhuǎn)換
????????
方法一:
- {"id":"0001","name":"hami"}
-{"id":"0002","name":"hami"}
方法二:
- id: "0001"name: hami1
-id: 0002name: hami2效果:[{"id":"0001","name":"hami"},{"id":"0002","name":"hami"}]
excel轉(zhuǎn)換為yaml格式
? ? ? ? ? ? ? ? 代碼如下
????????
"""
YAML 是 "YAML Ain't a Markup Language" -- 數(shù)據(jù)格式
YAML 的配置文件后綴為 .yml或.yam,如:huace.yml 。1.1 基本語法
- 大小寫敏感
- 使用縮進(jìn)表示層級(jí)關(guān)系
- 縮進(jìn)不允許使用tab,只允許空格
- 縮進(jìn)的空格數(shù)不重要,只要相同層級(jí)的元素左對(duì)齊即可
- '#' 表示注釋YAML 支持以下幾種數(shù)據(jù)類型:
- 對(duì)象:鍵值對(duì)的集合,又稱為映射(mapping)/ 哈希(hashes) / 字典(dictionary)
- 數(shù)組:一組按次序排列的值,又稱為序列(sequence) / 列表(list)
- 純量(scalars):單個(gè)的、不可再分的值 -- 任意的數(shù)據(jù)類型對(duì)象格式: key: value
數(shù)組格式: -可以在在線網(wǎng)址先確定格式的正確性:https://tool.p2hp.com/tool-format-yaml/安裝:pip install pyyaml
"""
# 1. 讀取數(shù)據(jù)
import yaml# file_path = "yamlData/test_yaml_04.yaml"
file_path = "yamlData/x_testFavor.yaml"
with open(file_path, 'r', encoding="utf-8") as file:data = yaml.safe_load(file)
print(data)# # 2. 修改數(shù)據(jù)
# data[1]["res"] = "執(zhí)行失敗"
#
# # 3. 寫入數(shù)據(jù)
# with open(file_path, 'w', encoding="utf-8") as file:
# # allow_unicode=True,避免將中文字符轉(zhuǎn)換為 Unicode 轉(zhuǎn)義序列
# yaml.dump(data, file, allow_unicode=True)
對(duì)yaml進(jìn)行讀寫
我們?cè)趯iT用來讀寫yaml和excel的py文件中新增一個(gè)方法
????????
@staticmethoddef write_yaml(data, file_path=YAMLDATA):"""寫入yaml文件,寫入無序沒有關(guān)系,通過key獲取數(shù)據(jù):param data: 需要寫入的數(shù)據(jù):param file_path: 文件路徑:return:"""with open(file_path, 'w', encoding='utf-8') as file:# 可以結(jié)合異常處理進(jìn)行封裝try:yaml.dump(data, file, allow_unicode=True)print("YAML數(shù)據(jù)寫入成功。")except yaml.YAMLError as e:print(f"YAML數(shù)據(jù)寫入失敗: {e}")
對(duì)框架進(jìn)行修改
?我們改成讀取yaml文件
????????對(duì)應(yīng)的讀取讀取數(shù)據(jù)源的方法需要修改成: AllCaseData = FileReader.read_yaml()
????????寫入到Y(jié)aml文件,所以無需行號(hào)去進(jìn)行記錄,可刪除變量: row、cell
????????因?yàn)橥ㄟ^Yaml讀取出來的本身就是json,所以 dict_data 這個(gè)變量值,有可能
是字典,也有可能是字符串,所以為了靈活起見,統(tǒng)一改成如下,同時(shí)也可以直接去掉eval方法即可
????????
# 擴(kuò)展:不做強(qiáng)制要求
# 字符串
# input_string = "{'name':'Alice', 'age':'25', 'city':'New York'}"
# 字典
input_string = {'name':'Alice', 'age':'25', 'city':'New York'}print("轉(zhuǎn)之前:", type(input_string)) # 字符串
# 如果input_string類型是str 則 轉(zhuǎn)成字典eval(input_string); 否則的話:直接輸出 ( else input_string)
result = eval(input_string) if isinstance(input_string, str) else input_string
print("轉(zhuǎn)之后:", type(result)) # 字典
print(result)
就是判斷值是字典還是字符串?是字符串就轉(zhuǎn)為字典
我們只需要在測(cè)試用例中修改讀寫的數(shù)據(jù)
????????
再根據(jù)情況修改上述的轉(zhuǎn)換格式的代碼?即可
回寫數(shù)據(jù)到y(tǒng)aml
????????首先我們寫入的時(shí)候是寫入所有的數(shù)據(jù)到data 當(dāng)中
如,我們所有讀取出來的數(shù)據(jù)是AllCaseData,比如如下格式
????????
[{id":0,"caseName":"登錄接口","result":nall},
{id":1,"caseName":"加入購物車","result":nall}]
通過參數(shù)化方式我們每次能夠拿到一條數(shù)據(jù),比如你要修改第一個(gè)這條數(shù)據(jù),可以
?
CaseData["result"] = value
重點(diǎn):但是你修改的只是這一條數(shù)據(jù),直接寫入就會(huì)不正確(覆蓋其它的數(shù)據(jù))。
所以,正確的做法是:這里的CaseData["id"] 代表下標(biāo)。
?
AllCaseData[CaseData["id"] ] = value
yaml+allure+pytest框架如下所示:
????????
import json
import pytest
from YamlOptimization.pytest_yaml_allure.common.FileDataDriver import FileReader
from YamlOptimization.pytest_yaml_allure.api_keyword.api_key import ApiKey
from YamlOptimization.pytest_yaml_allure.config import *
import allure
from jinja2 import Template # 變量渲染class TestCase:# 獲取對(duì)應(yīng)的數(shù)據(jù) CaseData 需要從文檔當(dāng)中去進(jìn)行讀取# 1. 獲取數(shù)據(jù)(四要素) 2. 發(fā)送請(qǐng)求 3.獲取響應(yīng)數(shù)據(jù) 4.斷言AllCaseData = FileReader.read_yaml()ak = ApiKey()# 定義:all_val 存放提取出的數(shù)據(jù)all_var = {}def __dynamic_title(self, CaseData):# # 動(dòng)態(tài)生成標(biāo)題# allure.dynamic.title(data[11])# 如果存在自定義標(biāo)題if CaseData["caseName"] is not None:# 動(dòng)態(tài)生成標(biāo)題allure.dynamic.title(CaseData["caseName"])if CaseData["storyName"] is not None:# 動(dòng)態(tài)獲取story模塊名allure.dynamic.story(CaseData["storyName"])if CaseData["featureName"] is not None:# 動(dòng)態(tài)獲取feature模塊名allure.dynamic.feature(CaseData["featureName"])if CaseData["remark"] is not None:# 動(dòng)態(tài)獲取備注信息allure.dynamic.description(CaseData["remark"])if CaseData["rank"] is not None:# 動(dòng)態(tài)獲取級(jí)別信息(blocker、critical、normal、minor、trivial)allure.dynamic.severity(CaseData["rank"])def __json_extraction(self, CaseData, res):"""提取響應(yīng)之后的數(shù)據(jù):param CaseData: 當(dāng)前的Case,主要獲取需要提取數(shù)據(jù)的字段:jsonExData:param res:響應(yīng)得到的對(duì)應(yīng)的結(jié)果:return:"""try:if CaseData["jsonExData"]:Exdata = CaseData["jsonExData"] # {"VAR_TOKEN":"$..token","MSG":"$.msg"}print("需要提取的數(shù)據(jù):>>>", Exdata)for key, value in Exdata.items():# 通過對(duì)應(yīng)的jsonpath獲取具體的數(shù)據(jù)new_value = self.ak.get_text(res.json(), value)self.all_var.update({key: new_value})print("提取出來的數(shù)據(jù):>>>", self.all_var)else:print("需要提取的數(shù)據(jù)為空")except Exception:print("請(qǐng)檢查你需要提取數(shù)據(jù)數(shù)據(jù)格式的正確性。")def __sql_extraction(self, CaseData):"""從數(shù)據(jù)庫提取數(shù)據(jù):param CaseData: 當(dāng)前的Case,主要獲取需要提取數(shù)據(jù)的字段: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-需要提取的數(shù)據(jù):>>>", Exdata)for key, value in Exdata.items():# 通過對(duì)應(yīng)的sql獲取具體的數(shù)據(jù)new_value = self.ak.get_sqlData(value)self.all_var.update({key: new_value})print("SQL-提取出來的數(shù)據(jù):>>>", self.all_var)else:print("SQL-需要提取的數(shù)據(jù)為空")except Exception:print("SQL-請(qǐng)檢查你需要提取數(shù)據(jù)數(shù)據(jù)格式的正確性。")def __sql_assertData(self, CaseData):res = Trueif CaseData["sqlAssertData"] and CaseData["sqlExpectResult"]:# 實(shí)際結(jié)果:從數(shù)據(jù)庫讀取出來的數(shù)據(jù)--字典的格式 # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}realityData = CaseData["sqlAssertData"]# 期望結(jié)果:{"name":"hami","id":75}expectData = CaseData["sqlExpectResult"]realityDataDict = {}for key, value in realityData.items():# 通過對(duì)應(yīng)的sql獲取具體的數(shù)據(jù)new_value = self.ak.get_sqlData(value)realityDataDict.update({key: new_value})if expectData != realityDataDict:res = Falsereturn 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的下標(biāo)row = CaseData["id"]# 初始化對(duì)應(yīng)的值:res = Nonemsg = Nonevalue = None# 知識(shí):是否斷言# is_assert = True# -------------------------發(fā)送請(qǐng)求-------------------------------try:# 請(qǐng)求數(shù)據(jù)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_ERRORCaseData["result"] = valueelse:# 得到對(duì)應(yīng)的響應(yīng)數(shù)據(jù)res = getattr(self.ak, CaseData["method"])(**dict_data)# -------------------------提取數(shù)據(jù)庫的操作---------------------------self.__sql_extraction(CaseData)# -------------------------進(jìn)行斷言處理-------------------------------# 實(shí)際結(jié)果try:msg = self.ak.get_text(res.json(), CaseData["actualResult"])except Exception:value = MSG_EXDATA_ERRORCaseData["result"] = valueelse:# 只會(huì)是一個(gè)分支語言,但是不會(huì)造成測(cè)試結(jié)果成功或者失敗,所以必須無論如何都是需要斷言if msg == CaseData["expectResult"]:value = MSG_ASSERT_OK# 成功之后進(jìn)行數(shù)據(jù)提取self.__json_extraction(CaseData, res)else:# is_assert = Falsevalue = MSG_ASSERT_NOCaseData["result"] = valuefinally:assert msg == CaseData["expectResult"], value# -------------------------進(jìn)行數(shù)據(jù)庫斷言處理-------------------------------try:res = self.__sql_assertData(CaseData) # False Trueexcept:print("SQL斷言出現(xiàn)問題")value = "SQL斷言出現(xiàn)問題"assert res, valueelse:assert resfinally:CaseData["result"] = value# -------------------------把當(dāng)前的CaseData值寫入:可以通過后置處理方法去處理-------------------------------self.AllCaseData[row] = CaseDataFileReader.write_yaml(self.AllCaseData)
4.大量響應(yīng)報(bào)文處理及加解密、簽名處理
? ? ? ? 1.全字段斷言-DeepDiff
????????deepdiff 是一個(gè)Python庫,用于比較Python數(shù)據(jù)結(jié)構(gòu)(例如字典、列表、JSON等)之間的差異。它不僅可以比較簡單的Python數(shù)據(jù)類型,還可以比較任意深度或復(fù)雜度的數(shù)據(jù)結(jié)構(gòu)。
在Python中,我們經(jīng)常需要比較兩個(gè)JSON對(duì)象的異同。例如測(cè)試中,我們需要比較預(yù)期輸出和實(shí)際輸出是否相同。而在開發(fā)中,我們也需要比較兩個(gè)JSON對(duì)象的差異以便維護(hù)。使用 deepdiff 庫,可以輕松處理這些場景
????????DeepDiff庫的主要模塊如下:
????????
????????1. deepdiff.DeepDiff :這是DeepDiff庫的核心模塊,提供了比較兩個(gè)對(duì)象之間差異的功能。它可以比較字典、列表、集合等復(fù)雜對(duì)象,并返回差異的詳細(xì)信息。
2. deepdiff.DeepSearch :這是一個(gè)工具類,用于在復(fù)雜對(duì)象中搜索指定值。它可以深度遍歷對(duì)象,并返回找到的匹配項(xiàng)的路徑信息。
3. deepdiff.DeepHash :這個(gè)模塊用于生成復(fù)雜對(duì)象的哈希值。它可以遞歸地計(jì)算對(duì)象的哈希值,并考慮對(duì)象中的差異
? 2.deepdiff常用操作
????????如果實(shí)際請(qǐng)求結(jié)果和預(yù)期值的json數(shù)據(jù)都一致,那么會(huì)返回 {} 空字典,否則會(huì)返回對(duì)比差異的結(jié)果,接口測(cè)試中我們也可以根據(jù)這個(gè)特點(diǎn)進(jìn)行斷言
????????如果對(duì)比結(jié)果不同,將會(huì)給出下面對(duì)應(yīng)的返回
????????1. type_changes:類型改變的key=
????????2. values_changed:值發(fā)生變化的key
????????3. dictionary_item_added:字典key添加
????????4. dictionary_item_removed:字段key刪除
? ? ? ? 案例如下
????????
from deepdiff import DeepDiff
"""
總結(jié):
1. 當(dāng)數(shù)據(jù)沒有差異的時(shí)候: 返回是一個(gè)空字典 {}
2. 當(dāng)數(shù)據(jù)有差異的情況,會(huì)根據(jù)實(shí)際情況顯示:1. type_changes:類型改變的key2. values_changed:值發(fā)生變化的key3. dictionary_item_added:字典key添加4. dictionary_item_removed:字段key刪除
"""# 期望結(jié)果
exjson = {'code': 0,"message": "成功","data": {"total": 28,"id": 123}
}# 實(shí)際結(jié)果
sjjson= {'code': 0,"message": "成功","data": {"total": 28,"id": 123}
}# 1. 如果兩個(gè)json 是一樣的情況下,返回的值是一個(gè)空字典
res = DeepDiff(exjson,sjjson)
print(res)實(shí)際結(jié)果
sjjson = {'code': "0", # 類型不一樣"message": "成功","data": {"total": 28,"id": 123}
}# 2. 類型錯(cuò)誤會(huì)提示:{'type_changes':XXXXXXXXX},root代表的是根節(jié)點(diǎn)
res = DeepDiff(exjson, sjjson)
print(res) # {'type_changes':XXXXXXXXX}
sjjson = {'code': 100, # 類型一樣,數(shù)據(jù)不一樣"message": "成功","data": {"total": 28,"id": 123}
}
3. 當(dāng)你的值有改變,會(huì)顯示 :values_changed
res = DeepDiff(exjson, sjjson)
print(res) # 'values_changed': {"root['code']": {'new_value': 100, 'old_value': 0}}}sjjson = {'code': 0, # 類型一樣,數(shù)據(jù)不一樣"message": "成功","data": {"total": 28,"id": 123},"add": "長沙"
}
res = DeepDiff(exjson, sjjson)
print(res) # {'dictionary_item_added': [root['add']]}sjjson = {"message": "成功","data": {"total": 28,"id": 123}
}
res = DeepDiff(exjson, sjjson)
print(res) # {'dictionary_item_removed': [root['code']]}
忽略排序、大小寫及某個(gè)字段\
其實(shí),在實(shí)際接口斷言中,可能需要校驗(yàn)的字段順序不一樣,又或者有一些字段值不需要,為了解決這類問題,Deepdiff也提供了相信的參數(shù),只需要在比較的時(shí)候加入,傳入對(duì)應(yīng)參數(shù)即可
例如?我們遇到一個(gè)相應(yīng)數(shù)據(jù):
期望結(jié)果:
{"code":"200","ordeid":"OR567456415645646",data:"241523415361123132132"}
實(shí)際結(jié)果:【未定】--- ID 是不確定的,是后端自動(dòng)生成的,過濾字段。
{"code":"200","ordeid":"OR567456415645646",data:"241523415361123132132"}
ignore_order(忽略排序)
ignore_string_case(忽略大小寫)
exclude_paths排除指定的字段
from deepdiff import DeepDiff"""
總結(jié):
1. 當(dāng)數(shù)據(jù)沒有差異的時(shí)候: 返回是一個(gè)空字典 {}
2. 當(dāng)數(shù)據(jù)有差異的情況,會(huì)根據(jù)實(shí)際情況顯示:1. type_changes:類型改變的key2. values_changed:值發(fā)生變化的key3. dictionary_item_added:字典key添加4. dictionary_item_removed:字段key刪除
"""
from deepdiff import DeepDiff# 1. 字典是以key,順序沒有關(guān)系
# json1 = {"name": "hami", "age": 18}
# json2 = {"age": 18, "name": "hami"}# res = DeepDiff(json1,json2)
# print(res)# 2. 定義兩個(gè)列表進(jìn)行比較,列表有序的,按照順序去進(jìn)行對(duì)比
# list1 = [1, 2, 3]
# list2 = [3, 2, 1]# 有序列表,會(huì)返回對(duì)應(yīng)的數(shù)據(jù): {'values_changed': {'root[0]': {'new_value': 3, 'old_value': 1}, 'root[2]': {'new_value': 1, 'old_value': 3}}}
# res = DeepDiff(list1, list2)
# print(res)
# # 過濾對(duì)應(yīng)的順序: ignore_order=True ------{}
# res = DeepDiff(list1, list2, ignore_order=True)
# print(res)# 3. 過濾大小寫的操作: ignore_string_case=True
# json1 = {"name": "hami", "age": 18}
# json2 = {"age": 18, "name": "Hami"}
#
# res = DeepDiff(json1, json2)
# print(res)
# # 過濾字符的大小寫(不區(qū)分)
# res = DeepDiff(json1, json2, ignore_string_case=True)
# print(res)# 4. 忽略某個(gè)字段 exclude_paths={"age"} --- 用的最多
json1 = {"code": "200", "name": "hami", "usercode": "431123456789", "age": 18}
json2 = {"code": "200", "name": "hami", "usercode": "431123456789", "age": 20}
res = DeepDiff(json1, json2)
print(res)
res = DeepDiff(json1, json2, exclude_paths={"age"})
# res = DeepDiff(json1, json2, exclude_paths={"root['age']"})
print(res)
? ? ? ??
? 其他參數(shù):
????????
from deepdiff import DeepDiff"""
總結(jié):
1. 當(dāng)數(shù)據(jù)沒有差異的時(shí)候: 返回是一個(gè)空字典 {}
2. 當(dāng)數(shù)據(jù)有差異的情況,會(huì)根據(jù)實(shí)際情況顯示:1. type_changes:類型改變的key2. values_changed:值發(fā)生變化的key3. dictionary_item_added:字典key添加4. dictionary_item_removed:字段key刪除
"""
from deepdiff import DeepDiff# 忽略數(shù)字
from decimal import Decimal
# 高精度的十進(jìn)制計(jì)算,避免了浮點(diǎn)數(shù)運(yùn)算中的精度丟失問題。
t1 = Decimal('10.01') # 數(shù)字類型
t2 = 10.01# print(DeepDiff(t1, t2))
# print(DeepDiff(t1, t2, ignore_numeric_type_changes=True))# 忽略字符串
res = DeepDiff(b'hello', 'hello', ignore_string_type_changes=True)
# print(res)# 打印出來的格式可以指定:
# TREE_VIEW = 'tree'
# TEXT_VIEW = 'text' # 默認(rèn)
t1 = {"name": "yanan", "pro": {"sh": "shandong", "city": ["zibo", "weifang"]}}
t2 = {"name": "yanan", "pro": {"sh": "shandong", "city": ["taian", "weifang"]}}
# 顯示格式:tree
diff1 = DeepDiff(t1, t2, view='tree')
print(diff1)
# 默認(rèn)為text # 可讀性
diff2 = DeepDiff(t1, t2, view='text')
print(diff2)
搜索模塊
?我們使用deepdiff還可以用來搜索
????????
from deepdiff import DeepSearchobj = ["long somewhere", "string", 0, "somewhere great!"]
item = 'some' # 大小寫敏感的
ds = DeepSearch(obj, item, case_sensitive=True)
print(ds)
?
返回的結(jié)果就是其下標(biāo)值?
哈希模塊-DeepHash
????????DeepHash模塊是DeepDiff庫中的一個(gè)模塊,用于生成復(fù)雜對(duì)象的哈希值。它可以遞歸地計(jì)算對(duì)象的哈希值,并考慮對(duì)象中的差異。可以方便地計(jì)算復(fù)雜對(duì)象的哈希值,并在需要時(shí)用于對(duì)象的唯一標(biāo)識(shí)或數(shù)據(jù)校驗(yàn)等用途
????????
from deepdiff import DeepHash
# 可以把對(duì)應(yīng)的Value 轉(zhuǎn)成對(duì)應(yīng)的Hasht1 = {"name": "yanan", "pro": {"sh": "shandong", "city": ["zibo", "weifang"]}}
res = DeepHash(t1)
print(res)
?如圖顯示,輸出的結(jié)果就是一串hash值
3.全字段對(duì)比
? ? ? ? ? ? ? ? deepdiff的封裝
????????????????????????
from deepdiff import DeepDiffdef jsonDeepDiff(json1, json2, **other):"""對(duì)比json數(shù)據(jù)的一致性:param json1: 期望結(jié)果:param json2: 實(shí)際結(jié)果:param other: 你想要寫的對(duì)應(yīng)的規(guī)則:return:"""res = DeepDiff(json1, json2, **other)if res == {}:return Trueelse:return False
將deepdiff結(jié)果之前的框架? ?
???????
? ? ? ? ? ? ? ? 我們?cè)赿eepdiff封裝在關(guān)鍵字封裝的類之中和對(duì)應(yīng)的測(cè)試用例中
????????????????
? ??
另外我們還要避免excel表中的值為空的情況,因此,我們需要再請(qǐng)求數(shù)據(jù)的地方進(jìn)行修改
????????
4.加密
核心思路:如果說對(duì)應(yīng)的參數(shù)需要進(jìn)行加密的話,在傳參的時(shí)候在key當(dāng)中通過 @ 符號(hào)進(jìn)行標(biāo)識(shí)
對(duì)我們的data對(duì)進(jìn)行遍歷,判斷key的[0] == @
例如:{"@name":"tony","@password":"123456"}
我們先把加密方法進(jìn)行封裝
????????
"""
對(duì)稱加密:加密和解密使用的是同一把鑰匙,即:使用相同的密匙對(duì)同一密碼進(jìn)行加密和解密。
常用的加密方法:DES、3DES、AES...(AES算法是目前最常用的對(duì)稱加密算法)
"""
import base64
from Crypto.Cipher import AESclass EncryptDate:# 構(gòu)造方法def __init__(self, key):# 類屬性self.key = key.encode("utf-8") # 初始化密鑰self.length = AES.block_size # 初始化數(shù)據(jù)塊大小 :16位self.aes = AES.new(self.key, AES.MODE_ECB) # 初始化AES,ECB模式的實(shí)例# 截?cái)嗪瘮?shù),去除填充的字符self.unpad = lambda date: date[0:-ord(date[-1])]# 缺幾位數(shù)據(jù)就補(bǔ)齊多少位數(shù)據(jù):16的倍數(shù)def pad(self, text): # text == tony"""#填充函數(shù),使被加密數(shù)據(jù)的字節(jié)碼長度是block_size的整數(shù)倍"""count = len(text.encode('utf-8')) # count = 4add = self.length - (count % self.length) # 求它們相差的位數(shù)# add = 16- (4%16) === 16 - 4 == 12entext = text + (chr(add) * add)# entext = “tony” + (chr(add) * 12 ) === entext == tony# print("entext的數(shù)據(jù)是:",entext)return entext# 加密函數(shù)def encrypt(self, encrData): # 加密函數(shù) 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# 解密函數(shù)def decrypt(self, decrData): # 解密函數(shù) XbXHJrNLwoTVcyfqM9eTgQ==# 從base64編碼轉(zhuǎn)回來res = base64.decodebytes(decrData.encode("utf8"))# 將數(shù)據(jù)進(jìn)行對(duì)應(yīng)的解密:XbXHJrNLwoTVcyfqM9eTgQ==msg = self.aes.decrypt(res).decode("utf8")# print("msg的值:",msg)# 把轉(zhuǎn)回來的數(shù)據(jù)后面的字符去掉。return self.unpad(msg)key = "1234567812345678"
ENCRYPTAES = EncryptDate(key)
然后我們?cè)龠M(jìn)行加密處理
?在excel和yaml的處理py文件中?寫上對(duì)應(yīng)的方法:
@staticmethoddef data_EncryptDateAES(data):newdata = {} # 去掉前面@符號(hào)同時(shí)數(shù)據(jù)進(jìn)行加密for key in data:if key[0] == "@":# 需要加密處理newdata[key[1:]] = ENCRYPTAES.encrypt(data[key])else:# 不需要加密處理newdata[key] = data[key]return newdata
5.接口簽名Sign封裝
????????數(shù)字簽名是一種用于驗(yàn)證數(shù)據(jù)完整性和身份認(rèn)證的密碼學(xué)技術(shù)。它通過使用私鑰對(duì)數(shù)據(jù)進(jìn)行加密來創(chuàng)建一個(gè)唯一的數(shù)字標(biāo)識(shí),該標(biāo)識(shí)稱為簽名。驗(yàn)證者可以使用相應(yīng)的公鑰對(duì)簽名進(jìn)行解密和驗(yàn)證,以確認(rèn)數(shù)據(jù)的完整性和真實(shí)性
token \ session \ cookie \ 簽名(sign) --- 都是用來鑒權(quán)的
它們的區(qū)別是什么?
token \ session \ cookie
token 其實(shí)用來代表是系統(tǒng)當(dāng)中的某個(gè)用戶 --- 一般正常情況下是寫在header
session \ cookie 是你發(fā)送請(qǐng)求的時(shí)候自帶的.瀏覽器會(huì)自帶這個(gè)對(duì)應(yīng)的數(shù)據(jù)
session \ cookie 區(qū)別是什么?
存儲(chǔ)位置不同: cookie -客戶端 ; session--服務(wù)端(會(huì)話)
大小限制不同: cookie - 有大小限制; session 沒有大小限制
安全隱患不同: cookie 存在安全隱患,可以攔截或找你本地文件得到你存儲(chǔ)的信息
時(shí)效性不同: cookie 是時(shí)效性, session關(guān)閉瀏覽器時(shí)就消失
簽名(sign) --- 是接口當(dāng)中的一個(gè)字段. 發(fā)送消息\發(fā)送文檔\保證安全性\需要在接口傳一波數(shù)據(jù).
通過算法得到一串?dāng)?shù)據(jù)(類似于token)