接口自动化测试模板

目录

[Python+pytest 自动化测试落地模板(接口场景)](#Python+pytest 自动化测试落地模板(接口场景))

[1. 标准目录结构(先建好)](#1. 标准目录结构(先建好))

[2. 各文件完整代码(直接复制)](#2. 各文件完整代码(直接复制))

(1)配置层:config/setting.py

(2)公共方法层:common/log_handler.py(日志封装)

(3)公共方法层:common/request_handler.py(请求封装)

(4)数据层:data/test_data.yaml(数据驱动)

(5)用例层:testcases/test_api.py(pytest用例)

(6)执行入口:run.py

(7)依赖清单:requirements.txt

[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. 模板使用步骤(落地关键)

  1. 安装依赖

    bash 复制代码
    pip install -r requirements.txt
    # 安装allure(需先装Java):https://allurereport.org/docs/install/
  2. 修改配置

    1. 替换setting.py中的BASE_URLtoken为你的项目实际值;

    2. 替换test_api.py中的接口路径、参数为业务接口;

    3. 补充test_data.yaml中的业务用例数据。

  3. 运行用例

    bash 复制代码
    # 直接运行
    python run.py
    # 切换环境运行(示例:生产环境)
    TEST_ENV=prod python run.py
  4. 查看报告 :打开reports/allure-html/index.html即可看到可视化报告。

相关推荐
小仙女的小稀罕24 分钟前
听不清重要会议录音急疯?这款常见AI工具听脑AI精准转译
开发语言·人工智能·python
书到用时方恨少!30 分钟前
Python random 模块使用指南:从入门到精通
开发语言·python
第一程序员34 分钟前
Python 4.0正式发布:新特性与学习建议
python·github
色空大师42 分钟前
【网站搭建实操(一)环境部署】
java·linux·数据库·mysql·网站搭建
亚历克斯神1 小时前
Flutter for OpenHarmony: Flutter 三方库 mutex 为鸿蒙异步任务提供可靠的临界资源互斥锁(并发安全基石)
android·数据库·安全·flutter·华为·harmonyos
IAUTOMOBILE1 小时前
用Python批量处理Excel和CSV文件
jvm·数据库·python
威联通安全存储1 小时前
破除“重前端、轻底层”的数字幻象:如何夯实工业数据的物理底座
前端·python
Amour恋空1 小时前
Java多线程
java·开发语言·python
小陈工2 小时前
2026年3月28日技术资讯洞察:5G-A边缘计算落地、低延迟AI推理革命与工业智造新范式
开发语言·人工智能·后端·python·5g·安全·边缘计算
常利兵2 小时前
Spring项目新姿势:Lambda封装Service调用,告别繁琐注入!
java·数据库·spring