pytest 邊學邊用(二)
準備工作完成後,就是用例的規劃了。我準備按照這個思路來:
- 先梳理出基礎的業務流程,找到相關介面,先寫這些介面的用例,寫完再根據業務流程做介面組合測試。 介面主要分為兩類:WEB 頁面呼叫後端的介面,提供給第三方的業務介面。 提供給第三方的業務介面,之前已經用 RF 寫過了,並且整合到了 jenkins。 這次主要就是處理 WEB 頁面呼叫後端的介面,從鑑權的角度,這類介面分成 2 小類,需要鑑權 or NOT。 因為只有我一個人,所以肯定是優先冒煙測試先完成。 整體的鑑權相關設計思路:
- 登入介面不要鑑權,該介面返回的 token 是除去它以外全部介面所必須的。所以使用者就弄兩個,一個用來測試登入,一個作為預置的測試賬戶,作為全域性 token 獲取使用者。
- 獲取 token 放在 tests 目錄下的 conftest.py 中,結果存到 redis 中失效時間用 token 的有效期。存之前先看能不能取出來,能取到就跳過登入獲取 token,直接返回取到的 token。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author : 小橙子的爸比 (279453094@qq.com)
# @Version : 1.0
# @Update Time : 2025/1/4 下午7:44
# @File : conftest.py
# @IDE : PyCharm
# @Desc : 檔案描述資訊
import json
import httpx
from jsonpath import jsonpath
from db.database_factory import DatabaseFactory
from utils.nb_logger import httpx_log
from utils.nb_logger import pytest_log
import pytest
from config.env_config import EnvInfo, ApiUri
def pytest_runtest_makereport(item, call):
"""鉤子函式收集失敗的斷言資訊存入日誌檔案"""
if call.excinfo is not None:
msg = {
"module": item.location[0],
"function": item.name,
"line": item.location[1],
"message": str(call.excinfo.value).replace("\n", ":")
}
pytest_log.error(json.dumps(msg, indent=4, ensure_ascii=False))
headers = EnvInfo().stitching_headers()
@pytest.fixture(scope="session", autouse=True)
def get_token(uri: str = ApiUri.LOGIN_URI, username: str = EnvInfo.USER_NAME,
password: str = EnvInfo.PASS_WORD):
"""負責全域性token預置,存入redis,有效期一週"""
data = {
"username": username,
"password": password,
"captchaKey": "b8cb0ef9-57d0-44d9-af53-aad6d5d00183",
"captcha": "1"
}
url = EnvInfo().stitching_url(uri)
with httpx.Client() as client:
response = client.post(url=url, headers=headers, json=data)
httpx_log.info(f"session級fixture POST請求(預置全域性token):{url}")
httpx_log.info(f"POST請求頭訊息:{headers}")
httpx_log.info(f"POST請求引數:{data}")
httpx_log.info(f"POST響應引數:{response.text}")
if response.status_code != 200:
raise httpx.HTTPError(f"狀態碼異常,預期值200,實際返回{response.status_code}")
token = jsonpath(response.json(), '$.idToken')
rds = DatabaseFactory().get_db_instance("redis").db_getter()
redis_key = EnvInfo.USER_NAME + "-" + EnvInfo.TEST_ENV_TAG + "-" + EnvInfo.REDIS_STORE_TOKEN_KEY
rds.set(redis_key, token[0], ex=86400)
return response
考慮到後期的維護,儘量不用硬編碼,能存到配置檔案的全存進去。
這個寫完登入測試用例跟著改改就行了,需要說明的就是冒煙用例跟異常用例只是驅動資料不同,我決定使用 yam 檔案來儲存測試資料,一個檔案就能搞定。
非鑑權介面就這麼一個,就不去封裝了。
鑑權 token 在頭訊息裡,全域性的前置指令碼只是保障 redis 中的 token 存在
封裝的 httpx 請求,會去 redis 裡取 token,並塞進頭訊息裡,這個放到下一個需要鑑權的介面再看具體的例項。
先看看這個介面測試用例:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author : 小橙子的爸比 (279453094@qq.com)
# @Version : 1.0
# @Update Time : 2025/1/3 下午5:55
# @File : test_login.py
# @IDE : PyCharm
# @Desc : 檔案描述資訊
import os
import pytest
from utils.nb_logger import pytest_log as log
import allure
from project_config import settings as config
from utils.tools import YamlTool
import httpx
from config.env_config import EnvInfo, ApiUri, TestDataYamlFileName
from jsonpath import jsonpath
headers = EnvInfo().stitching_headers()
def api_login(url: str, username: str, password: str):
data = {
"username": username,
"password": password,
"captchaKey": "b8cb0ef9-57d0-44d9-af53-aad6d5d00183",
"captcha": "1"
}
with httpx.Client() as client:
response = client.post(url=url, headers=headers, json=data)
log.info(f"smarthub登入請求返回資料:{response.json()}")
return response
yml = YamlTool(str(os.path.join(config.system.DATA_PATH, TestDataYamlFileName.LOGIN_YML)))
datas = yml.read_yaml()
@allure.feature("SMARTHUB介面測試")
@allure.story("登入介面測試")
@allure.testcase("正確的使用者名稱密碼可以獲取token")
@pytest.mark.smock
@user18ize("user_info", datas['user_info'])
def test_login(user_info):
username = user_info['username']
password = user_info['password']
title = user_info['title']
# 動態標題
allure.dynamic.title(title)
url = EnvInfo().stitching_url(ApiUri.LOGIN_URI)
resp = api_login(url, username, password)
assert resp.status_code == 200
# 使用jsonpath獲取返回值中的指定內容
token = jsonpath(resp.json(), '$.idToken')
assert token is not None
@allure.feature("SMARTHUB介面測試")
@allure.story("登入介面測試")
@allure.testcase("錯誤的使用者名稱密碼無法獲取token")
@user22y
@user23ize("user_info_error", datas['user_info_error'])
def test_login_error(user_info_error):
print(user_info_error)
username = user_info_error['username']
password = user_info_error['password']
title = user_info_error['title']
# 動態標題
allure.dynamic.title(title)
except_value = user_info_error['except_value']
url = EnvInfo().stitching_url(ApiUri.LOGIN_URI)
resp = api_login(url, username, password)
# 使用jsonpath獲取返回值中的指定內容
title = jsonpath(resp.json(), '$.title')
assert resp.status_code == 400
assert except_value == title[0]
if __name__ == '__main__':
pytest.main()
測試報告展示一下:
附贈一個 allure 的標記說明圖
相關文章
- SpringBoot邊學邊用(二)SpringBoot 整合 Mybatis(xml檔案形式)Spring BootMyBatisXML
- Kotlin邊用邊學:Inline Functions的適用場景KotlininlineFunction
- [邊學邊練]用簡單例項學習React單例React
- iOS 邊學邊記iOS
- 邊學邊玩CSS GridCSS
- 【Python | 邊學邊敲邊記】第二次:深度&&廣度優先演算法Python演算法
- 邊聊邊複製,邊修改邊測試,利用chatgpt用laravel框架做一個部落格應用ChatGPTLaravel框架
- 二分查詢左邊界,右邊界,>=,>,<=,<
- 邊學邊寫——母函式及其在中學數學競賽中的運用(一)函式
- 【從0到1學習邊緣容器系列2】之 邊緣應用管理
- 【Python | 邊敲邊學邊記】第一次:正規表示式Python
- 【Python | 邊學邊敲邊記】第三次:URL去重策略Python
- 【二分】【邊界判定】
- [學習筆記] 邊學教程邊建“知識付費”應用 第1次總結筆記
- 【Python | 邊學邊敲邊記】第四次:初識爬蟲框架ScrapyPython爬蟲框架
- 邊下載邊播放的播放器Android邊下邊播播放器Android
- 邊學邊做的第一個Unity小遊戲Unity遊戲
- 【從0到1學習邊緣容器系列-3】應用容災之邊緣自治
- 打破邊界,邊緣計算有何應用場景?
- ASP.NET CORE 邊學邊記之 SwaggerUI簡單配置ASP.NETSwaggerUI
- 視覺化學習:利用向量判斷多邊形邊界視覺化
- thinkphp3.23寫的個人部落格,邊學邊改吧!PHP
- iOS邊下邊播總結iOS
- 邊緣應用場景需求興起,FusionCube解碼智慧邊緣!
- 【從0到1學習邊緣容器系列1】之 邊緣計算與邊緣容器的起源
- 小白也能操作Linux,十分鐘教你簡單的shell指令碼編寫邊學邊用Linux指令碼
- 邊做邊學入門微信小程式之仿豆瓣評分微信小程式
- 在風變程式設計學習Python,找到了邊玩邊學的樂趣程式設計Python
- python去除圖片白邊黑邊Python
- 雲原生與邊緣計算的碰撞——邊緣原生應用實踐
- 評分9.7!京東斷貨王的Python神書,邊看邊用!Python
- Minecraft 從入門到入坑(邊玩遊戲邊學程式設計)Raft遊戲程式設計
- 風變程式設計邊學習邊實操,讓過程更快樂!程式設計
- CSS 邊框陰影立體邊框CSS
- Unity 利用Cache實現邊下邊玩Unity
- 多邊形填充-活動邊表法
- Unity URP 描邊 用RenderPassFeature實現Unity
- opencv學習之邊緣檢測OpenCV