单元测试(Unit Test)对代码中的最小可测单元(如函数、类方法)进行验证,在修改代码后快速发现回归错误。pytest 是 Python 生态中最主流的测试框架之一,语法简洁、支持 fixture 与参数化、插件丰富。本文介绍 pytest 的安装与基础、用例编写、fixture、参数化、覆盖率及与 CI 的集成,帮助开发者建立可持续的自动化测试体系。
一、单元测试与 pytest 为何重要
根据多项软件工程研究,持续运行单元测试可显著降低线上缺陷率;在重构或加功能时,测试可提供安全网。pytest 在 PyPI 下载量长期位居测试类前列,社区与文档成熟。
| 能力 | 说明 |
|---|---|
| 自动发现 | 默认收集 test_ 开头或 _test 结尾的函数、Test 开头的类 |
| 断言 | 使用 assert 即可,失败时自动输出 diff |
| fixture | 依赖注入式准备数据与环境 |
| 参数化 | 一组用例多组数据,减少重复代码 |
| 插件 | pytest-cov 覆盖率、pytest-xdist 并行等 |
数据来源:PyPI 下载统计、pytest 官方文档(综合整理)。
二、pytest 安装与基础
安装:pip install pytest pytest-cov。pytest-cov 用于生成覆盖率报告。项目结构建议:源码放 src 或包名目录,测试放 tests/,测试文件以 test_ 开头或 _test 结尾。
2.1 运行测试
pytest:运行当前目录及子目录下所有测试。pytest tests/ -v:只跑 tests 目录,-v 显示详细用例名。pytest tests/test_foo.py -k "test_add":按名称过滤。失败时可用 pytest --pdb 进入调试。
2.2 断言
直接写 assert 表达式即可,例如 assert result == 42、assert "error" in str(exc)。pytest 会重写 assert,失败时输出左右值对比。
三、fixture 与参数化
fixture 用于准备测试依赖(如数据库连接、临时文件、mock 对象),通过函数参数注入到用例中。
3.1 定义与使用 fixture
使用 @pytest.fixture 装饰器定义。用例通过同名参数接收,pytest 自动调用 fixture 并注入。scope 可选 function(默认)、class、module、session。
3.2 conftest.py
conftest.py 中的 fixture 无需导入,同目录及子目录下的用例均可使用。常用于定义全局或目录级共享 fixture(如数据库、配置)。
3.3 参数化
@pytest.mark.parametrize("a,b,expected", [(1,2,3), (0,0,0)]):多组输入与期望,减少重复。可与 fixture 组合使用。
| 用法 | 说明 |
|---|---|
| @pytest.fixture | 定义可复用资源,支持 scope |
| @pytest.mark.parametrize | 多组参数驱动,参数名与用例参数一致 |
| conftest.py | 共享 fixture,无需 import |
| pytest.raises(SomeError) | 断言抛出指定异常 |
四、覆盖率与 CI 集成
运行 pytest --cov=src --cov-report=html --cov-report=term 可生成终端与 HTML 覆盖率报告。--cov-fail-under=80 可在覆盖率低于 80% 时令 pytest 失败。在 CI 中:每次提交或 PR 触发 pytest,覆盖率低于阈值则阻断合并。可参考本站《CI/CD 与持续交付实践》。
五、测试设计要素权重
基于实际项目经验,单元测试落地时以下要素的影响程度(相对权重,满分 100):
说明:权重基于 pytest 项目实践归纳,仅供参考。
六、小结
pytest 通过 fixture 与参数化提升测试复用与可维护性,结合覆盖率与 CI 可实现持续验证。建议从核心业务逻辑开始写测试,逐步提高覆盖率。若需前端 E2E 或端到端测试思路,可阅读《前端性能监控与可观测性》;若需完整自动化流水线,可参考《CI/CD 与持续交付实践》。