目录
[Python+pytest 自动化测试落地模板(接口场景)](#Python+pytest 自动化测试落地模板(接口场景))
[1. 标准目录结构(先建好)](#1. 标准目录结构(先建好))
[2. 各文件完整代码(直接复制)](#2. 各文件完整代码(直接复制))
(2)公共方法层:common/log_handler.py(日志封装)
(3)公共方法层:common/request_handler.py(请求封装)
(4)数据层:data/test_data.yaml(数据驱动)
(5)用例层:testcases/test_api.py(pytest用例)
[3. 模板使用步骤(落地关键)](#3. 模板使用步骤(落地关键))
Python+pytest 自动化测试落地模板(接口场景)
这套模板遵循测开最佳实践,采用分层设计(配置层、公共方法层、用例层、数据层),易维护、易扩展,适配绝大多数接口自动化场景。
1. 标准目录结构(先建好)

auto_test_project/
├── config/ # 配置层:全局配置、环境变量 │ └── setting.py # 基础配置(域名、请求头、超时等)
├── common/ # 公共方法层:封装通用逻辑 │ ├── request_handler.py # 请求封装(GET/POST/异常处理) │ └── log_handler.py # 日志封装
├── testcases/ # 用例层:业务用例 │ └── test_api.py # 接口用例(pytest格式) ├── data/ # 数据层:测试数据(数据驱动) │ └── test_data.yaml # 用例数据(yaml格式)
├── reports/ # 报告层:Allure报告(自动生成)
├── run.py # 执行入口:运行用例、生成报告
└── requirements.txt # 依赖清单
2. 各文件完整代码(直接复制)
(1)配置层:config/setting.py
python
# 全局配置,区分测试/生产环境
import os
# 环境切换(可通过环境变量控制)
ENV = os.getenv("TEST_ENV", "test") # 默认测试环境
# 域名配置
BASE_URL = {
"test": "https://test-api.xxx.com",
"prod": "https://prod-api.xxx.com"
}[ENV]
# 请求配置
REQUEST_HEADERS = {
"Content-Type": "application/json",
"token": "your_test_token" # 可替换为动态获取逻辑
}
TIMEOUT = 10 # 请求超时时间(秒)
# 日志配置
LOG_LEVEL = "INFO"
LOG_FILE = "./logs/auto_test.log"
# Allure报告路径
ALLURE_REPORT_PATH = "./reports/allure-results"
ALLURE_HTML_PATH = "./reports/allure-html"
(2)公共方法层:common/log_handler.py(日志封装)
python
import logging
import os
from config.setting import LOG_LEVEL, LOG_FILE
# 创建日志目录
os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True)
# 初始化日志配置
def init_logger():
logger = logging.getLogger("auto_test")
logger.setLevel(LOG_LEVEL)
# 避免重复添加处理器
if logger.handlers:
return logger
# 文件处理器
file_handler = logging.FileHandler(LOG_FILE, encoding="utf-8")
# 控制台处理器
console_handler = logging.StreamHandler()
# 日志格式
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s"
)
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 添加处理器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
return logger
# 全局日志对象
logger = init_logger()
(3)公共方法层:common/request_handler.py(请求封装)
python
import requests
from config.setting import BASE_URL, REQUEST_HEADERS, TIMEOUT
from common.log_handler import logger
class RequestHandler:
"""封装HTTP请求,含异常处理、日志记录"""
def __init__(self):
self.base_url = BASE_URL
self.headers = REQUEST_HEADERS
self.timeout = TIMEOUT
def request(self, method, url, **kwargs):
"""
通用请求方法
:param method: GET/POST/PUT/DELETE
:param url: 接口路径(无需域名)
:param kwargs: 其他参数(data/json/params等)
:return: 响应对象
"""
# 拼接完整URL
full_url = f"{self.base_url}{url}"
# 合并请求头
if "headers" in kwargs:
self.headers.update(kwargs.pop("headers"))
try:
logger.info(f"请求:{method} {full_url},参数:{kwargs}")
response = requests.request(
method=method.upper(),
url=full_url,
headers=self.headers,
timeout=self.timeout,
**kwargs
)
logger.info(f"响应:状态码{response.status_code},内容:{response.json()}")
return response
except requests.exceptions.Timeout:
logger.error(f"请求超时:{full_url}")
raise Exception(f"请求超时({self.timeout}秒):{full_url}")
except requests.exceptions.ConnectionError:
logger.error(f"连接失败:{full_url}")
raise Exception(f"连接失败:{full_url}")
except Exception as e:
logger.error(f"请求异常:{str(e)}")
raise e
# 快捷方法
def get(self, url, **kwargs):
return self.request("GET", url, **kwargs)
def post(self, url, **kwargs):
return self.request("POST", url, **kwargs)
# 全局请求对象
req = RequestHandler()
(4)数据层:data/test_data.yaml(数据驱动)
html
# 接口用例数据,key对应用例函数名
test_query_order:
- case_name: 正常查询有效订单
order_no: "OD20260211001"
expected_code: 200
expected_msg: "查询成功"
- case_name: 查询不存在的订单
order_no: "OD9999999999"
expected_code: 200
expected_msg: "订单不存在"
test_create_order:
- case_name: 正常创建订单
amount: 99.9
goods_id: "G1001"
expected_code: 200
expected_msg: "创建成功"
(5)用例层:testcases/test_api.py(pytest用例)
python
import pytest
import allure
import yaml
from common.request_handler import req
from common.log_handler import logger
# 加载测试数据
def load_test_data(file_path):
with open(file_path, encoding="utf-8") as f:
return yaml.safe_load(f)
test_data = load_test_data("./data/test_data.yaml")
@allure.epic("订单模块")
@allure.feature("订单查询接口")
class TestQueryOrder:
@pytest.mark.parametrize("case", test_data["test_query_order"])
@allure.story("订单查询")
def test_query_order(self, case):
"""订单查询接口用例"""
allure.dynamic.title(case["case_name"])
# 接口请求
response = req.get(
url="/api/order/query",
params={"orderNo": case["order_no"]}
)
# 断言
assert response.status_code == case["expected_code"]
assert response.json()["msg"] == case["expected_msg"]
@allure.epic("订单模块")
@allure.feature("订单创建接口")
class TestCreateOrder:
@pytest.mark.parametrize("case", test_data["test_create_order"])
@allure.story("订单创建")
def test_create_order(self, case):
"""订单创建接口用例"""
allure.dynamic.title(case["case_name"])
# 接口请求
response = req.post(
url="/api/order/create",
json={"amount": case["amount"], "goodsId": case["goods_id"]}
)
# 断言
assert response.status_code == case["expected_code"]
assert response.json()["msg"] == case["expected_msg"]
(6)执行入口:run.py
python
import os
import pytest
from config.setting import ALLURE_REPORT_PATH, ALLURE_HTML_PATH
def run_tests():
# 清空旧报告
os.system(f"rm -rf {ALLURE_REPORT_PATH}/* {ALLURE_HTML_PATH}/*")
# 运行pytest用例,生成Allure结果
pytest.main([
"-s", "-v",
"testcases/",
f"--alluredir={ALLURE_REPORT_PATH}",
"--clean-alluredir" # 清理旧报告
])
# 生成Allure HTML报告
os.system(f"allure generate {ALLURE_REPORT_PATH} -o {ALLURE_HTML_PATH} --clean")
# 打开报告(可选)
# os.system(f"allure open {ALLURE_HTML_PATH}")
if __name__ == "__main__":
run_tests()
(7)依赖清单:requirements.txt
pytest==7.4.3 requests==2.31.0 PyYAML==6.0.1 allure-pytest==2.13.2 logging==0.4.9.6
3. 模板使用步骤(落地关键)
-
安装依赖:
bashpip install -r requirements.txt # 安装allure(需先装Java):https://allurereport.org/docs/install/ -
修改配置:
-
替换
setting.py中的BASE_URL、token为你的项目实际值; -
替换
test_api.py中的接口路径、参数为业务接口; -
补充
test_data.yaml中的业务用例数据。
-
-
运行用例:
bash# 直接运行 python run.py # 切换环境运行(示例:生产环境) TEST_ENV=prod python run.py -
查看报告 :打开
reports/allure-html/index.html即可看到可视化报告。