接口自动化测试模板

目录

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

相关推荐
Suryxin.2 小时前
从0开始复现nano-vllm「model_runner.py」上半篇之初始化分布式推理环境
人工智能·python·深度学习·机器学习·vllm
奔跑的蜗牛FelixChioa2 小时前
python学习之快速掌握 pandas 数据可视化:「matplotlib+seaborn」极简实战方案
python·pandas·数据可视化
想睡hhh2 小时前
redis的高效工作方式
数据库·redis·缓存
、BeYourself2 小时前
PostgreSQL 安装中文全文检索插件zhparser扩展
数据库·postgresql·全文检索
桂花饼2 小时前
Sora-2 API 低成本接入指南:Python 实现 0.08 元/次的视频生成方案
人工智能·python·qwen3-next·nano banana pro·gemini-3-pro·sora2pro
dishugj2 小时前
【Oracle】Oracle rac1 节点ora.chad offline解决方案
数据库·oracle
木子02042 小时前
oracle里面inner join 和left join 的区别
数据库·oracle
数据库人生2 小时前
Oracle RANGE分区表 HIGH_VALUE 获取
数据库·oracle·dbms_xmlgen·high_value
他们叫我技术总监2 小时前
Oracle 11g 实战进阶:LONG类型字段超长模糊查询终极方案(附表空间GB级监控优化)
数据库·oracle