4. pytest 框架基础:核心特性、对比与配置运行

pytest 是 Python 生态中最流行的测试框架之一,其简洁的语法、强大的 fixture 机制、丰富的插件生态以及与 unittest 的高度兼容性,使其成为 API 测试与 Mock 隔离的首选框架。本章将系统讲解 pytest 的核心特性,并通过对比与配置实践,帮助你快速掌握 pytest 在 API 治理中的应用。

4.1 pytest 核心特性

pytest 的设计哲学是“约定优于配置”,它通过自动发现测试文件与函数、提供灵活的 fixture 管理、支持参数化测试以及可扩展的插件系统,大幅降低了测试编写与维护的成本。

4.1.1 Fixture:测试依赖管理

Fixture 是 pytest 最强大的特性之一,用于管理测试所需的上下文、资源或状态(如数据库连接、HTTP 客户端、Mock 对象)。与 unittest 的 setUp/tearDown 不同,pytest fixture 支持模块化、作用域控制、自动清理以及依赖注入。

核心优势:Fixture 通过 @pytest.fixture 装饰器定义,测试函数通过参数名直接注入,无需继承或手动调用。支持 scope 参数(function、class、module、package、session)控制生命周期,yield 关键字实现资源释放。
import pytest
from unittest.mock import Mock

# 定义一个 session 级别的 fixture,模拟 API 客户端
@pytest.fixture(scope="session")
def api_client():
    client = Mock()
    client.get.return_value = {"status": "ok"}
    yield client
    # 清理逻辑(如有)
    print("清理 API 客户端资源")

# 测试函数直接注入 fixture
def test_get_user(api_client):
    response = api_client.get("/users/1")
    assert response["status"] == "ok"

4.1.2 参数化:数据驱动测试

参数化允许用同一测试函数验证多组输入输出,避免重复代码。pytest 通过 @pytest.mark.parametrize 实现,支持笛卡尔积组合与自定义 ID。

import pytest

# 单参数参数化
@pytest.mark.parametrize("input_val, expected", [
    (1, 2),
    (2, 4),
    (3, 6),
])
def test_double(input_val, expected):
    assert input_val * 2 == expected

# 多参数组合(笛卡尔积)
@pytest.mark.parametrize("method", ["GET", "POST"])
@pytest.mark.parametrize("endpoint", ["/users", "/orders"])
def test_api_endpoints(method, endpoint):
    # 验证不同 HTTP 方法与端点的组合
    assert method in ["GET", "POST"]

4.1.3 插件系统:生态扩展

pytest 拥有超过 1000 个第三方插件,覆盖 Mock、覆盖率、并行执行、报告生成等场景。常用插件包括:

插件名称 功能描述 安装命令
pytest-mock 提供 mocker fixture,简化 Mock 对象创建 pip install pytest-mock
pytest-cov 集成 coverage.py,生成测试覆盖率报告 pip install pytest-cov
pytest-xdist 支持多 CPU 并行执行测试 pip install pytest-xdist
pytest-html 生成美观的 HTML 测试报告 pip install pytest-html
实践建议:在 API 测试项目中,推荐安装 pytest-mock 和 pytest-cov。前者简化 Mock 隔离,后者帮助评估测试对 API 接口的覆盖程度。
pytest 核心特性架构 pytest 引擎 Fixture 管理 依赖注入 / 作用域 / 清理 参数化测试 数据驱动 / 笛卡尔积 插件生态 Mock / 覆盖率 / 并行 核心设计原则 约定优于配置 · 自动发现 · 简洁断言 · 高度可扩展

4.2 pytest 与 unittest 对比

unittest 是 Python 标准库中的测试框架,采用类继承和断言方法;pytest 则更灵活、简洁。下表从多个维度对比两者差异:

对比维度 pytest unittest
测试编写风格 函数式,无需继承,使用 assert 语句 类继承 unittest.TestCase,使用 self.assertEqual() 等方法
Fixture 管理 @pytest.fixture,支持作用域、自动清理、依赖注入 setUp()/tearDown(),类级别需 setUpClass()
参数化支持 原生支持 @pytest.mark.parametrize 需借助 subTest() 或第三方库
插件生态 丰富,超过 1000 个插件 有限,主要依赖标准库
测试发现 自动递归发现 test_*.py 或 *_test.py 需手动指定或使用 TestLoader
断言失败信息 详细、智能,显示实际值与期望值差异 基础,需自定义消息
兼容性 可直接运行 unittest 测试用例(通过 pytest 命令) 无法直接运行 pytest 测试
迁移建议:如果现有项目使用 unittest,无需重写所有测试。pytest 可以原生运行 unittest 测试用例,允许逐步迁移。新项目建议直接使用 pytest,以获得更简洁的语法和更强大的特性。

4.3 pytest 配置与运行

pytest 支持通过命令行参数、配置文件(pytest.ini、pyproject.toml、tox.ini)以及 conftest.py 进行灵活配置。

4.3.1 配置文件示例

推荐使用 pytest.ini 或 pyproject.toml 集中管理配置,避免每次运行都输入冗长参数。

# pytest.ini 示例
[pytest]
# 指定测试文件匹配模式
python_files = test_*.py *_test.py
# 添加自定义标记
markers =
    slow: 标记慢速测试
    smoke: 冒烟测试
# 设置测试路径
testpaths = tests
# 禁用某些插件
addopts = -p no:cacheprovider
# pyproject.toml 示例(现代项目推荐)
[tool.pytest.ini_options]
minversion = "7.0"
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
markers = [
    "slow: 标记慢速测试",
    "smoke: 冒烟测试",
]
addopts = [
    "-v",
    "--tb=short",
    "--strict-markers",
]

4.3.2 常用运行命令

命令 说明
pytest 自动发现并运行所有测试
pytest -v 详细输出,显示每个测试的名称与结果
pytest -k "test_login" 按名称表达式筛选测试
pytest -m smoke 运行标记为 smoke 的测试
pytest --co 仅收集测试用例,不执行
pytest --tb=long 显示完整回溯信息
pytest -x 遇到第一个失败即停止
pytest --maxfail=3 最多允许 3 个失败后停止

4.3.3 conftest.py:共享 Fixture 与钩子

conftest.py 是 pytest 的“本地插件”文件,可以放置在测试目录的任何层级。它自动被 pytest 加载,用于定义共享 fixture、钩子函数或命令行选项。

# tests/conftest.py
import pytest
from unittest.mock import Mock

@pytest.fixture(scope="session")
def mock_api_client():
    """全局 Mock API 客户端,所有测试共享"""
    client = Mock()
    client.get.return_value = {"status": "ok", "data": []}
    return client

@pytest.fixture
def auth_header():
    """每个测试函数独立的认证头"""
    return {"Authorization": "Bearer test-token-123"}
最佳实践:将全局 Mock 对象、数据库连接、外部服务桩等放在 conftest.py 中,避免重复定义。利用 scope="session" 减少资源创建开销,但注意 Mock 对象的状态隔离。
pytest 配置与运行流程 配置来源 pytest.ini / pyproject.toml / tox.ini / conftest.py / 命令行参数 测试发现与收集 测试执行(并行/顺序) 报告生成(终端/HTML/XML) 配置优先级:命令行 > conftest.py > 配置文件 > 默认值

4.4 本章小结

  • Fixture 是 pytest 管理测试依赖的核心机制,支持作用域控制与自动清理,适合 Mock 隔离场景。
  • 参数化 通过 @pytest.mark.parametrize 实现数据驱动测试,减少重复代码。
  • 插件生态 提供 Mock、覆盖率、并行等扩展能力,pytest-mock 是 API 测试的必备插件。
  • 与 unittest 相比,pytest 更简洁、灵活,且兼容现有 unittest 测试。
  • 通过 pytest.ini 或 pyproject.toml 集中管理配置,conftest.py 实现共享 fixture 与钩子。