Python + Pytest接口自動(dòng)化測(cè)試方案的實(shí)現(xiàn)
前言
Postman 僅適合單接口調(diào)試,面對(duì)批量回歸、多環(huán)境驗(yàn)證和持續(xù)集成場(chǎng)景,手工操作效率極低且易出錯(cuò)。本文帶你從零搭建一套企業(yè)級(jí)可落地的 Python + Pytest 接口自動(dòng)化測(cè)試框架,覆蓋接口封裝、數(shù)據(jù)驅(qū)動(dòng)、報(bào)告可視化、CI/CD 集成全流程,可直接用于小型項(xiàng)目,也可平滑擴(kuò)展至大型分布式系統(tǒng)。
技術(shù)選型與優(yōu)勢(shì)對(duì)比
我們選擇以下技術(shù)棧,兼顧易用性、擴(kuò)展性和生態(tài)成熟度:
| 技術(shù)工具 | 核心作用 | 選型優(yōu)勢(shì) |
|---|---|---|
| Python 3.8+ | 腳本開(kāi)發(fā)語(yǔ)言 | 語(yǔ)法簡(jiǎn)潔、第三方庫(kù)豐富、測(cè)試領(lǐng)域生態(tài)完善 |
| Requests | HTTP 請(qǐng)求發(fā)送 | 最流行的 HTTP 客戶端,API 簡(jiǎn)單易用 |
| Pytest | 測(cè)試用例管理與執(zhí)行 | 比 unittest 更靈活,支持參數(shù)化、fixture、插件擴(kuò)展 |
| Allure | 測(cè)試報(bào)告生成 | 可視化效果好,支持用例分類、失敗截圖、歷史趨勢(shì) |
| YAML | 測(cè)試數(shù)據(jù)與配置管理 | 可讀性強(qiáng),適合存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù) |
| Jenkins | 持續(xù)集成 | 開(kāi)源免費(fèi),支持定時(shí)構(gòu)建、代碼觸發(fā)、報(bào)告集成 |
標(biāo)準(zhǔn)化項(xiàng)目結(jié)構(gòu)
采用分層設(shè)計(jì)思想,將配置、接口、用例、工具、數(shù)據(jù)分離,保證框架的可維護(hù)性:
api_auto_test/ ├── config/ # 環(huán)境配置目錄 │ └── config.yaml # 多環(huán)境配置(開(kāi)發(fā)/測(cè)試/生產(chǎn)) ├── api/ # 接口封裝層(所有業(yè)務(wù)接口) │ ├── __init__.py │ └── login_api.py # 登錄接口封裝 ├── testcases/ # 測(cè)試用例層(僅寫用例邏輯) │ ├── __init__.py │ └── test_login.py # 登錄模塊測(cè)試用例 ├── utils/ # 工具層(通用方法) │ ├── __init__.py │ ├── request_util.py # HTTP 請(qǐng)求封裝 │ ├── assert_util.py # 統(tǒng)一斷言工具 │ └── log_util.py # 日志工具(可選) ├── data/ # 測(cè)試數(shù)據(jù)層(數(shù)據(jù)驅(qū)動(dòng)) │ └── login_data.yaml # 登錄模塊測(cè)試數(shù)據(jù) ├── reports/ # 測(cè)試報(bào)告輸出目錄 ├── requirements.txt # 項(xiàng)目依賴清單 └── pytest.ini # Pytest 全局配置文件
環(huán)境搭建與依賴安裝
1. 基礎(chǔ)環(huán)境要求
- Python 3.8 及以上版本
- pip 包管理工具
2. 安裝依賴包
執(zhí)行以下命令一鍵安裝所有依賴:
pip install requests pytest pyyaml allure-pytest
3. 生成依賴清單
方便后續(xù)團(tuán)隊(duì)協(xié)作和 CI 集成:
pip freeze > requirements.txt
核心模塊實(shí)現(xiàn)
5.1 多環(huán)境配置管理
在 config/config.yaml 中配置多環(huán)境信息,支持一鍵切換:
# 環(huán)境配置:dev-開(kāi)發(fā)環(huán)境 test-測(cè)試環(huán)境 prod-生產(chǎn)環(huán)境
active_env: "test"
env:
dev:
base_url: "https://dev-api.example.com"
timeout: 10
test:
base_url: "https://api.example.com"
timeout: 10
prod:
base_url: "https://prod-api.example.com"
timeout: 155.2 通用請(qǐng)求工具封裝
在 utils/request_util.py 中封裝 HTTP 請(qǐng)求,增加異常處理和日志打?。?/p>
import requests
import yaml
import logging
from typing import Dict, Any
# 配置日志
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
class RequestUtil:
def __init__(self):
# 加載配置文件
with open("../config/config.yaml", "r", encoding="utf-8") as f:
self.config = yaml.safe_load(f)
# 獲取當(dāng)前激活的環(huán)境
self.active_env = self.config["active_env"]
self.base_url = self.config["env"][self.active_env]["base_url"]
self.timeout = self.config["env"][self.active_env]["timeout"]
def send_request(
self,
method: str,
path: str,
headers: Dict[str, str] = None,
params: Dict[str, Any] = None,
json: Dict[str, Any] = None,
data: Any = None,
**kwargs
) -> requests.Response:
"""
統(tǒng)一發(fā)送 HTTP 請(qǐng)求
:param method: 請(qǐng)求方法 GET/POST/PUT/DELETE
:param path: 接口路徑
:param headers: 請(qǐng)求頭
:param params: URL 參數(shù)
:param json: JSON 格式請(qǐng)求體
:param data: 表單格式請(qǐng)求體
:return: 響應(yīng)對(duì)象
"""
url = self.base_url + path
logger.info(f"請(qǐng)求地址: {method} {url}")
logger.info(f"請(qǐng)求參數(shù): params={params}, json={json}")
try:
resp = requests.request(
method=method,
url=url,
headers=headers,
params=params,
json=json,
data=data,
timeout=self.timeout,
**kwargs
)
logger.info(f"響應(yīng)狀態(tài)碼: {resp.status_code}")
logger.info(f"響應(yīng)內(nèi)容: {resp.text[:500]}") # 只打印前500字符,避免日志過(guò)長(zhǎng)
return resp
except requests.exceptions.Timeout:
logger.error(f"請(qǐng)求超時(shí): {url}")
raise
except requests.exceptions.ConnectionError:
logger.error(f"連接失敗: {url}")
raise
except Exception as e:
logger.error(f"請(qǐng)求異常: {str(e)}")
raise
5.3 業(yè)務(wù)接口層封裝
在 api/login_api.py 中封裝登錄接口,遵循一個(gè)接口一個(gè)方法的原則:
from utils.request_util import RequestUtil
class LoginApi:
def __init__(self):
self.request = RequestUtil()
def login(self, username: str, password: str):
"""
登錄接口
:param username: 用戶名
:param password: 密碼
:return: 響應(yīng)對(duì)象
"""
path = "/api/v1/login"
payload = {
"username": username,
"password": password
}
return self.request.send_request(
method="POST",
path=path,
json=payload
)
5.4 數(shù)據(jù)驅(qū)動(dòng)測(cè)試數(shù)據(jù)
在 data/login_data.yaml 中管理測(cè)試數(shù)據(jù),實(shí)現(xiàn)數(shù)據(jù)與腳本分離:
- case_name: 正常登錄-正確用戶名密碼 username: "test01" password: "123456" expect_status: 200 expect_code: 0 expect_msg: "登錄成功" - case_name: 登錄失敗-密碼錯(cuò)誤 username: "test01" password: "wrong123" expect_status: 200 expect_code: 1001 expect_msg: "密碼錯(cuò)誤" - case_name: 登錄失敗-用戶名不存在 username: "nonexist" password: "123456" expect_status: 200 expect_code: 1002 expect_msg: "用戶不存在" - case_name: 登錄失敗-用戶名為空 username: "" password: "123456" expect_status: 200 expect_code: 1003 expect_msg: "用戶名不能為空"
5.5 測(cè)試用例編寫
在 testcases/test_login.py 中編寫測(cè)試用例,使用 Pytest 參數(shù)化實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng):
import pytest
import yaml
from api.login_api import LoginApi
# 加載測(cè)試數(shù)據(jù)
def load_login_data():
with open("../data/login_data.yaml", "r", encoding="utf-8") as f:
return yaml.safe_load(f)
class TestLogin:
def setup_class(self):
"""測(cè)試類執(zhí)行前的初始化操作"""
self.login_api = LoginApi()
@pytest.mark.parametrize("case", load_login_data(), ids=[case["case_name"] for case in load_login_data()])
def test_login_cases(self, case):
"""登錄接口測(cè)試用例"""
# 發(fā)送請(qǐng)求
resp = self.login_api.login(
username=case["username"],
password=case["password"]
)
# 斷言
assert resp.status_code == case["expect_status"]
assert resp.json()["code"] == case["expect_code"]
assert case["expect_msg"] in resp.json()["msg"]
5.6 統(tǒng)一斷言工具
在 utils/assert_util.py 中封裝常用斷言方法,統(tǒng)一斷言邏輯:
import requests
from typing import Any
def assert_status_code(resp: requests.Response, expect_code: int = 200):
"""斷言響應(yīng)狀態(tài)碼"""
assert resp.status_code == expect_code, f"狀態(tài)碼錯(cuò)誤,預(yù)期:{expect_code},實(shí)際:{resp.status_code}"
def assert_response_code(resp: requests.Response, expect_code: int = 0):
"""斷言業(yè)務(wù)響應(yīng)碼"""
assert resp.json()["code"] == expect_code, f"業(yè)務(wù)碼錯(cuò)誤,預(yù)期:{expect_code},實(shí)際:{resp.json()['code']}"
def assert_response_contains(resp: requests.Response, key: str, value: Any):
"""斷言響應(yīng)體包含指定鍵值對(duì)"""
assert key in resp.json(), f"響應(yīng)體中不存在鍵:{key}"
assert resp.json()[key] == value, f"鍵值錯(cuò)誤,預(yù)期:{value},實(shí)際:{resp.json()[key]}"
def assert_response_msg_contains(resp: requests.Response, expect_msg: str):
"""斷言響應(yīng)消息包含指定內(nèi)容"""
assert expect_msg in resp.json()["msg"], f"響應(yīng)消息不包含:{expect_msg},實(shí)際:{resp.json()['msg']}"
測(cè)試執(zhí)行與報(bào)告生成
1. 基礎(chǔ)執(zhí)行命令
在項(xiàng)目根目錄執(zhí)行以下命令運(yùn)行所有測(cè)試用例:
pytest -v
2. 生成 Allure 測(cè)試報(bào)告
# 執(zhí)行測(cè)試并生成 Allure 原始數(shù)據(jù) pytest -v --alluredir=reports/allure-results # 啟動(dòng)本地服務(wù)查看報(bào)告 allure serve reports/allure-results # 生成靜態(tài) HTML 報(bào)告(可部署到服務(wù)器) allure generate reports/allure-results -o reports/allure-report --clean
Pytest 全局配置
在 pytest.ini 中配置 Pytest 全局參數(shù),簡(jiǎn)化執(zhí)行命令:
[pytest]
# 默認(rèn)命令行參數(shù):-s 輸出print信息 --tb=short 簡(jiǎn)化錯(cuò)誤堆棧
addopts = -s --tb=short --alluredir=reports/allure-results
# 指定測(cè)試用例搜索路徑
testpaths = testcases
# 指定測(cè)試文件命名規(guī)則
python_files = test_*.py
# 指定測(cè)試類命名規(guī)則
python_classes = Test*
# 指定測(cè)試方法命名規(guī)則
python_functions = test_*
# 標(biāo)記用例(用于分組執(zhí)行)
markers =
smoke: 冒煙測(cè)試用例
regression: 回歸測(cè)試用例Jenkins CI/CD 持續(xù)集成
將框架集成到 Jenkins,實(shí)現(xiàn)代碼提交自動(dòng)觸發(fā)測(cè)試和定時(shí)回歸測(cè)試:
- 安裝插件:在 Jenkins 插件管理中安裝
Allure Plugin、Git Plugin - 新建自由風(fēng)格項(xiàng)目
- 配置源碼管理:填寫 Git 倉(cāng)庫(kù)地址和分支
- 添加構(gòu)建步驟:執(zhí)行 Shell 命令
# 安裝依賴 pip install -r requirements.txt # 執(zhí)行測(cè)試 pytest
- 配置構(gòu)建后操作:添加
Allure Report,指定報(bào)告路徑為reports/allure-results - 可選配置:
- 構(gòu)建觸發(fā)器:設(shè)置定時(shí)構(gòu)建(如每天凌晨 2 點(diǎn)執(zhí)行回歸測(cè)試)
- 構(gòu)建通知:配置郵件通知,構(gòu)建失敗時(shí)發(fā)送郵件給相關(guān)人員
常見(jiàn)問(wèn)題 FAQ
Allure 安裝失敗怎么辦?
- Windows:下載 Allure 二進(jìn)制包,解壓后將 bin 目錄添加到系統(tǒng)環(huán)境變量
- Mac:執(zhí)行
brew install allure - Linux:執(zhí)行
sudo apt install allure
運(yùn)行用例提示文件路徑錯(cuò)誤?
- 確保在項(xiàng)目根目錄執(zhí)行 pytest 命令
- 將相對(duì)路徑改為絕對(duì)路徑,或使用
os.path模塊動(dòng)態(tài)獲取路徑
中文亂碼問(wèn)題?
- 在打開(kāi)文件時(shí)指定
encoding="utf-8" - 在 pytest.ini 中添加
env = LANG=zh_CN.UTF-8
- 在打開(kāi)文件時(shí)指定
如何處理需要 Token 的接口?
- 將登錄獲取 Token 的操作封裝為 Fixture,在需要的用例中引用
- 將 Token 保存到全局變量或配置文件中,供后續(xù)接口使用
結(jié)語(yǔ)
接口自動(dòng)化不是簡(jiǎn)單的“寫腳本”,而是構(gòu)建一套可持續(xù)維護(hù)、可擴(kuò)展、可集成的質(zhì)量保障體系。本文提供的方案是一個(gè)基礎(chǔ)框架,你可以根據(jù)項(xiàng)目實(shí)際需求進(jìn)行擴(kuò)展,比如增加數(shù)據(jù)庫(kù)操作、接口簽名、文件上傳下載等功能。
到此這篇關(guān)于Python + Pytest接口自動(dòng)化測(cè)試方案的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Python Pytest接口自動(dòng)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 使用python+requests+pytest實(shí)現(xiàn)接口自動(dòng)化
- Python+Requests+PyTest+Excel+Allure?接口自動(dòng)化測(cè)試實(shí)戰(zhàn)
- python+pytest接口自動(dòng)化之session會(huì)話保持的實(shí)現(xiàn)
- python+pytest接口自動(dòng)化參數(shù)關(guān)聯(lián)
- python+pytest接口自動(dòng)化之日志管理模塊loguru簡(jiǎn)介
- python+pytest接口自動(dòng)化之token關(guān)聯(lián)登錄的實(shí)現(xiàn)
- python使用pytest接口自動(dòng)化測(cè)試的使用
- python+requests+pytest接口自動(dòng)化的實(shí)現(xiàn)示例
相關(guān)文章
Python機(jī)器學(xué)習(xí)之基于Pytorch實(shí)現(xiàn)貓狗分類
看了許多關(guān)于PyTorch的入門文章,大抵是從torchvision.datasets中自帶的數(shù)據(jù)集進(jìn)行訓(xùn)練,導(dǎo)致很難把PyTorch運(yùn)用于自己的數(shù)據(jù)集上,真正地靈活運(yùn)用PyTorch,本文詳細(xì)介紹了怎么利用Pytorch實(shí)現(xiàn)貓狗分類,需要的朋友可以參考下2021-06-06
使用python接受tgam的腦波數(shù)據(jù)實(shí)例
這篇文章主要介紹了使用python接受tgam的腦波數(shù)據(jù)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04
使用Pandas處理時(shí)間序列數(shù)據(jù)Time Series詳解
SQLAlchemy是Python中最流行的ORM(對(duì)象關(guān)系映射)框架之一,它提供了高效且靈活的數(shù)據(jù)庫(kù)操作方式,本文將介紹如何使用SQLAlchemy ORM進(jìn)行數(shù)據(jù)庫(kù)操作,有需要的可以了解下2025-11-11
python之PyInstaller(將Python腳本打包為可執(zhí)行文件方式)
PyInstaller將Python腳本打包為跨平臺(tái)可執(zhí)行文件,自動(dòng)處理依賴庫(kù),支持單文件/目錄模式,便于分發(fā),適用于GUI、數(shù)據(jù)文件處理及多平臺(tái)部署,優(yōu)化體積與權(quán)限問(wèn)題2025-09-09
django2+uwsgi+nginx上線部署到服務(wù)器Ubuntu16.04
這篇文章主要介紹了django2+uwsgi+nginx上線部署到服務(wù)器Ubuntu16.04,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
python神經(jīng)網(wǎng)絡(luò)AlexNet分類模型訓(xùn)練貓狗數(shù)據(jù)集
這篇文章主要為大家介紹了python神經(jīng)網(wǎng)絡(luò)AlexNet分類模型訓(xùn)練貓狗數(shù)據(jù)集,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05

