pytest
pytest 的核心体系可以归纳为 6 大支柱
- 测试发现与命名约定
pytest 靠命名规则自动收集测试,无需手动注册:
| 规则 | 要求 |
|---|---|
| 文件名 | test_*.py 或 *_test.py |
| 函数/方法名 | test_ 开头 |
| 类名 | Test 开头(且不能有 __init__) |
- 自定义收集规则可在 pyproject.toml 中通过 [tool.pytest.ini_options] 配置。
- Fixture(测试夹具)
这是 pytest 最核心的依赖注入机制:
点击查看代码
@pytest.fixture(scope="session") # 作用域控制
def db(): ...
@pytest.fixture(autouse=True) # 自动应用
def mock_env(): ...
@pytest.fixture(params=["mysql", "pg"]) # 参数化 fixture
def database(request): ...
核心要点:
- 通过参数名匹配自动注入
- yield 实现 setup + teardown
- 支持 function / class / module / package / session 五级作用域
- conftest.py 中的 fixture 对同目录及子目录自动可见
- Assert 断言
pytest 不需要 self.assertEqual() 这类专用断言方法,直接用原生 assert:
点击查看代码
assert result == expected
assert "error" in message
assert isinstance(obj, dict)
assert path.exists()
- 失败时 pytest 会自动 introspect 左右两边的值并给出详细对比,比 unittest 的断言信息丰富得多。
- Mark 标记系统
用装饰器给测试打标签,实现分类、跳过、参数化等:
采用了 类(Class)+ Fixture 的组织模式。这种写法是企业级 Python 项目中测试的“最佳实践”范式
AAA 结构:Arrange → Act → Assert 三段式是测试的黄金法则,保证每个测试只做一件事、只验证一个行为。
1.
| 标记 | 作用 | 典型场景 |
|---|---|---|
@pytest.mark.parametrize |
参数化:一个测试函数自动运行多组输入 | 边界值、多格式、多语言测试 |
@pytest.mark.skip |
无条件跳过 | 功能未实现、已知 bug 暂不修 |
@pytest.mark.skipif |
条件跳过 | 特定 OS/Python 版本/缺少依赖时跳过 |
@pytest.mark.xfail |
预期失败 | 已知 bug 但先记录,修复后自动提醒 |
@pytest.mark.asyncio |
异步测试 | 让 pytest 用事件循环运行 async def |
| 标记 | 作用 | 配合命令 |
|---|---|---|
@pytest.mark.integration |
自定义:集成测试 | pytest -m integration |
@pytest.mark.slow |
自定义:慢测试 | pytest -m "not slow" |
@pytest.mark.unit |
自定义:单元测试 | pytest -m unit |
@pytest.mark.timeout(N) |
超时保护(需插件) | 防止死锁/无限循环 |
@pytest.mark.order(N) |
执行顺序(需插件) | 依赖前置条件的测试 |
| 标记 | 作用 | 注意事项 |
|---|---|---|
@pytest.mark.usefixtures |
类/模块级批量应用 fixture | 当测试函数不需要直接使用 fixture 返回值时用 |
@pytest.mark.filterwarnings |
控制警告行为 | 忽略第三方库的无害警告 |
@pytest.mark.tryfirst / trylast |
控制 hook/fixture 执行优先级 | 插件开发或复杂 fixture 依赖时使用 |
@pytest.mark.no_cover |
排除覆盖率统计(需插件) | 纯装饰性代码、调试辅助函数 |
例如:
@pytest.mark.integration = 给测试贴一个"我是集成测试"的标签
[tool.pytest.ini_options]
markers = [
"integration: 需要外部服务的集成测试",
"slow: 运行时间较长的测试",
"unit: 纯单元测试",
]
需要标记测试?
├── 多组输入 → parametrize
├── 不想跑 → skip / skipif
├── 知道会挂 → xfail
├── 异步代码 → asyncio
├── 分类管理 → 自定义 (integration/slow/unit)
├── 防卡死 → timeout
└── 批量注入依赖 → usefixtures
pytest -m "integration" # 只跑集成测试
pytest -m "not slow" # 跳过慢测试
pytest -m "unit and not slow" # 快速的单元测试
pytest -m "integration or e2e" # 集成或端到端
pytest --strict-markers # CI 中推荐:未注册的标记直接报错
- 内置 Fixture
pytest 自带一批开箱即用的 fixture,无需定义:
| Fixture | 用途 |
|---|---|
tmp_path / tmp_path_factory |
临时目录(自动清理) |
capsys / capfd |
捕获 stdout/stderr |
monkeypatch |
安全地修改环境变量、属性、字典 |
request |
获取当前测试的元信息(参数、mark、nodeid) |
recwarn |
捕获警告 |
cache |
跨测试运行的持久化缓存 |
- 插件生态
pytest 的强大很大程度上来自插件,以下是事实标准级别的:
| 插件 | 作用 |
|---|---|
pytest-cov |
覆盖率报告 |
pytest-mock |
mocker fixture,替代 unittest.mock |
pytest-xdist |
多进程并行执行测试 |
pytest-asyncio |
异步测试支持 |
pytest-html |
HTML 测试报告 |
pytest-random-order |
随机执行顺序,发现隐式依赖 |
pytest-benchmark |
性能基准测试 |
安装后零配置即可使用(大部分通过 entry point 自动注册)。
知识优先级建议
如果你正在学习 pytest,建议按以下顺序掌握:
assert + 命名约定 → 能写最基本的测试
fixture + yield → 解决 setup/teardown 和资源管理
parametrize → 消除重复测试代码
内置 fixture(tmp_path, monkeypatch, capsys)→ 避免造轮子
mark + 命令行筛选(-k, -m)→ 灵活控制测试运行
插件 → 按需引入,不要一开始就装一堆
- 运行测试的常用命令
点击查看代码
uv run pytest # 跑所有
uv run pytest -v # 详细输出(每个测试一行)
uv run pytest -x # 第一个失败就停
uv run pytest -k "run_sql" # 只跑名字含 run_sql 的
uv run pytest src/test/test_run_sql.py # 只跑这个文件
uv run pytest src/test/test_run_sql.py::test_run_sql_rejects_non_select # 只跑这一个
uv run pytest --lf # 只跑上次失败的
uv run pytest -s # 显示 print 输出
uv run pytest --cov=mssql_mcp # 覆盖率(需要 pytest-cov)

浙公网安备 33010602011771号