接口自动化测试模板

目录

[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即可看到可视化报告。

相关推荐
helloweilei1 天前
python 抽象基类
python
用户8356290780511 天前
Python 实现 PPT 转 HTML
后端·python
IvorySQL1 天前
PostgreSQL 技术日报 (3月6日)|为什么 Ctrl-C 在 psql 里让人不安?
数据库·postgresql·开源
NineData1 天前
数据库管理工具NineData,一年进化成为数万+开发者的首选数据库工具?
运维·数据结构·数据库
zone77391 天前
004:RAG 入门-LangChain读取PDF
后端·python·面试
zone77391 天前
005:RAG 入门-LangChain读取表格数据
后端·python·agent
IvorySQL1 天前
PostgreSQL 技术日报 (3月5日)|规划器控制力升级,内核能力再进阶
数据库·postgresql·开源
树獭非懒2 天前
AI大模型小白手册|Embedding 与向量数据库
后端·python·llm
数据组小组2 天前
免费数据库管理工具深度横评:NineData 社区版、Bytebase 社区版、Archery,2026 年开发者该选哪个?
数据库·测试·数据库管理工具·数据复制·迁移工具·ninedata社区版·naivicat平替