原始版本
簡書:https://www.jianshu.com/p/6bfaca87a93b
博客園:https://www.cnblogs.com/zy7y/p/13426816.html
testerhome:https://testerhome.com/topics/25003
最新用例截圖以及用例填寫格式
數據依賴/路徑參數依賴
我理解的參數依賴/接口依賴就是接口進行關聯操作,比如有些查詢接口需要登錄之后才可以操作,那么我們就需要拿到token之類的東西,這一部分東西是放到header中的,apiAutoTest圍繞的只有路徑參數依賴,請求數據依賴
-
路徑參數依賴
譬如說現在的restful,一個users接口,路由一般這樣的
users
他的請求方式是get,這個路由我們把他認為是查所有用戶,如果查某一個用戶可能是這樣的users/:id
也是個get請求,這里這個id想表達的意思是這里有個需要個用戶id的參數,比如1-500里面的任意1個,也就是說這個id是可變的,可以從登錄接口的返回響應取一個叫userId的值 -
請求參數依賴
這個應該好理解些,就是說支付接口需要的訂單id,是從上一步提交訂單接口返回的響應訂單id
舉個例子
假設現在有個實際響應結果字典如下
{"case_002": {
"data": {
"id": 500,
"username": "admin",
"mobile": "12345678",
}},
"case_005": {
"data": {
"id": 511,
"create_time": 1605711095
},
}
}
-
excel中接口路徑內容:
users/&$.case_005.data.id&/state/&$.case_005.data.careate_time&
代碼內部解析后如下:
users/511/state/1605711095
&$.case_005.data.id&
代表從響應字典中提取case_005字典中data字典中的id的值,提取出來的結果是511 -
excel中請求參數內容如下:
{ "pagenum": 1, "pagesize": "12", "data": &$.case_005.data&, "userId": &$.case_002.data.id& }
代碼內部解析后如下:
{ "pagenum": 1, "pagesize": "12", "data": { "id": 511, "create_time": 1605711095 }, "userId": 500 }
其實不難看出其中規則&jsonpath提取語法&
,如果你需要的內容是字符串類型,只需要這樣"&jsonpath提取語法&"
上傳文件
用例中書寫格式,在上傳文件欄
# 單文件上傳在excel中寫法
{"接口中接受文件對象的參數名": "文件路徑地址"}
# 多文件上傳在excel中寫法
{"接口中接受文件對象的參數名": ["文件路徑1", "文件路徑2"]}
預期結果
用例書寫格式
# 斷言一個內容
{"jsonpath提取表達式": 預期結果內容}
# 多個斷言
{"jsonpath提取表達式1": 預期結果內容1,"jsonpath提取表達式2": 預期結果內容2}
其他優化
- config.yaml文件中新增可配置初始header,整體代碼優化,相比之前,同樣測試用例執行下,快了2s左右
- 將配置文件讀取,用例讀取整合在
read_file.py
下 - 移除報告壓縮方法
- 減少日志信息
現依賴處理代碼
tools/init.py
#!/usr/bin/env/python3
# -*- coding:utf-8 -*-
"""
@project: apiAutoTest
@author: zy7y
@file: __init__.py
@ide: PyCharm
@time: 2020/7/31
"""
import json
import re
import allure
from jsonpath import jsonpath
from loguru import logger
def extractor(obj: dict, expr: str = '.') -> object:
"""
根據表達式提取字典中的value,表達式, . 提取字典所有內容, $.case 提取一級字典case, $.case.data 提取case字典下的data
:param obj :json/dict類型數據
:param expr: 表達式, . 提取字典所有內容, $.case 提取一級字典case, $.case.data 提取case字典下的data
$.0.1 提取字典中的第一個列表中的第二個的值
"""
try:
result = jsonpath(obj, expr)[0]
except Exception as e:
logger.error(f'提取不到內容,丟給你一個錯誤!{e}')
result = None
return result
def rep_expr(content: str, data: dict, expr: str = '&(.*?)&') -> str:
"""從請求參數的字符串中,使用正則的方法找出合適的字符串內容並進行替換
:param content: 原始的字符串內容
:param data: 在該項目中一般為響應字典,從字典取值出來
:param expr: 查找用的正則表達式
return content: 替換表達式后的字符串
"""
for ctt in re.findall(expr, content):
content = content.replace(f'&{ctt}&', str(extractor(data, ctt)))
return content
def convert_json(dict_str: str) -> dict:
"""
:param dict_str: 長得像字典的字符串
return json格式的內容
"""
try:
if 'None' in dict_str:
dict_str = dict_str.replace('None', 'null')
elif 'True' in dict_str:
dict_str = dict_str.replace('True', 'true')
elif 'False' in dict_str:
dict_str = dict_str.replace('False', 'false')
dict_str = json.loads(dict_str)
except Exception as e:
if 'null' in dict_str:
dict_str = dict_str.replace('null', 'None')
elif 'true' in dict_str:
dict_str = dict_str.replace('true', 'True')
elif 'False' in dict_str:
dict_str = dict_str.replace('false', 'False')
dict_str = eval(dict_str)
logger.error(e)
return dict_str
def allure_title(title: str) -> None:
"""allure中顯示的用例標題"""
allure.dynamic.title(title)
def allure_step(step: str, var: str) -> None:
"""
:param step: 步驟及附件名稱
:param var: 附件內容
"""
with allure.step(step):
allure.attach(json.dumps(var, ensure_ascii=False, indent=4), step, allure.attachment_type.TEXT)
tools/data_process.py
#!/usr/bin/env/python3
# -*- coding:utf-8 -*-
"""
@project: apiAutoTest
@author: zy7y
@file: data_process.py
@ide: PyCharm
@time: 2020/11/18
"""
from tools import logger, extractor, convert_json, rep_expr, allure_step
from tools.read_file import ReadFile
class DataProcess:
response_dict = {}
header = ReadFile.read_config('$.request_headers')
have_token = header.copy()
@classmethod
def save_response(cls, key: str, value: object) -> None:
"""
保存實際響應
:param key: 保存字典中的key,一般使用用例編號
:param value: 保存字典中的value,使用json響應
"""
cls.response_dict[key] = value
logger.info(f'添加key: {key}, 對應value: {value}')
@classmethod
def handle_path(cls, path_str: str) -> str:
"""路徑參數處理
:param path_str: 帶提取表達式的字符串 /&$.case_005.data.id&/state/&$.case_005.data.create_time&
上述內容表示,從響應字典中提取到case_005字典里data字典里id的值,假設是500,后面&$.case_005.data.create_time& 類似,最終提取結果
return /511/state/1605711095
"""
# /&$.case.data.id&/state/&$.case_005.data.create_time&
return rep_expr(path_str, cls.response_dict)
@classmethod
def handle_header(cls, token: str) -> dict:
"""處理header
:param token: 寫: 寫入token到header中, 讀: 使用帶token的header, 空:使用不帶token的header
return
"""
if token == '讀':
return cls.have_token
else:
return cls.header
@classmethod
def handler_files(cls, file_obj: str) -> object:
"""file對象處理方法
:param file_obj: 上傳文件使用,格式:接口中文件參數的名稱:"文件路徑地址"/["文件地址1", "文件地址2"]
實例- 單個文件: &file&D:
"""
if file_obj == '':
return
for k, v in convert_json(file_obj).items():
# 多文件上傳
if isinstance(v, list):
files = []
for path in v:
files.append((k, (open(path, 'rb'))))
else:
# 單文件上傳
files = {k: open(v, 'rb')}
return files
@classmethod
def handle_data(cls, variable: str) -> dict:
"""請求數據處理
:param variable: 請求數據,傳入的是可轉換字典/json的字符串,其中可以包含變量表達式
return 處理之后的json/dict類型的字典數據
"""
if variable == '':
return
data = rep_expr(variable, cls.response_dict)
variable = convert_json(data)
return variable
@classmethod
def assert_result(cls, response: dict, expect_str: str):
""" 預期結果實際結果斷言方法
:param response: 實際響應字典
:param expect_str: 預期響應內容,從excel中讀取
return None
"""
expect_dict = convert_json(expect_str)
index = 0
for k, v in expect_dict.items():
actual = extractor(response, k)
index += 1
logger.info(f'第{index}個斷言,實際結果:{actual} | 預期結果:{v} \n斷言結果 {actual == v}')
allure_step(f'第{index}個斷言', f'實際結果:{actual} = 預期結果:{v}')
assert actual == v
源碼地址
master: 分支為最新代碼
version1.0: 分支為之前開源的代碼(通過字典迭代的方式來處理數據依賴)
Https://gitee.com/zy7y/apiAutoTest.git
Https://github.com/zy7y/apiAutoTest.git
后續打算
目前在公司正在做接口測試,說實話也是摸索着來,以上的優化項都是實際做的過程中突然想到的,然后就更新了
- 接入用例前后置SQL, 前置SQL目前想的是現在項目中遇到的問題,有些接口沒有返回需要的數據,這里就要用前置SQL查詢的結果傳到請求數據里面了,后置SQL主要是請求后查看數據庫中的數據是否變動,形成數據庫斷言 2020/12/08 完成 , 至此 apiAutoTest 應該不會 再有大更新~~~ 謝謝 看這個Demo的人
- 企業微信推送:目前項目中預想的效果,是后端人員提交代碼,自動部署之后,通過gitlab-ci 啟動測試代碼,進行接口測試完成之后采集allure中的測試結果一有異常/失敗用例就發送郵件並進行企業微信推送給領導 這個其實就是重新發送請求~
- .... 就不說了還有很多優化項,能力不夠好好充電吧,~~
致謝
謝謝各位對apiAutoTest的幫助,謝謝~~~