ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

pytest 入门与实践:从第一个测试到可维护的测试体系

pytest 入门与实践:从第一个测试到可维护的测试体系 pytest 入门与实践从第一个测试到可维护的测试体系面向 Python 开发者与测试工程师的实战指南涵盖安装、发现规则、断言、fixture、参数化、标记、配置、常用命令和团队实践。阅读对象刚开始写 Python 自动化测试的开发者、测试工程师以及希望改进测试项目的团队。预计阅读约 5 分钟 ·示例环境Python 3.10、pytesthttps://github.com/lfl171/pytest_zhishi.git目录pytest 是什么安装与第一个测试测试发现与命名assert 与失败信息Fixture组织前置条件和清理参数化Marker给测试分类与筛选常用内置能力配置与命令行推荐项目结构与实践常见问题小结1. pytest 是什么pytest 是 Python 生态中广泛使用的测试框架。它适用于从小型单元测试到集成测试的多种场景也能运行许多基于unittest的现有测试。主要特点如下低门槛普通函数配合 Python 原生assert即可编写测试无需继承特定测试基类。清晰的失败诊断断言失败时展示表达式中的实际值便于定位问题。Fixture 机制复用依赖与资源准备并管理生命周期和清理。参数化与标记扩展输入组合并按类别选择测试。插件生态接入覆盖率、并行执行、浏览器自动化等能力。pytest 是测试运行与组织工具不会自动判断产品需求是否覆盖充分。测试质量仍取决于清晰的预期、有效的边界分析和可靠的测试数据。2. 安装与第一个测试推荐在项目虚拟环境中安装避免依赖污染系统 Pythonpython-mvenv .venv# Windows PowerShell.venv\Scripts\Activate.ps1# macOS / Linux: source .venv/bin/activatepython-mpipinstallpytest创建test_calculator.pydefadd(a:int,b:int)-int:returnabdeftest_add_two_positive_numbers():assertadd(2,3)5在项目根目录运行python-mpytest成功时通常会看到1 passed。使用python -m pytest可明确通过当前 Python 环境执行模块也可以直接运行pytest。3. 测试发现与命名默认情况下pytest 会递归发现符合约定的测试文件、类和函数文件名通常是test_*.py或*_test.py。函数名以test_开头。测试类以Test开头且通常不定义__init__。类中的测试方法以test_开头。明确且一致的命名让收集行为可预测。可用pytest --collect-only先检查将运行哪些测试只运行某个文件、类或函数时可用节点路径pytest --collect-only pytest tests/test_user.py pytest tests/test_user.py::TestUser::test_create_user测试函数名宜描述行为和场景例如test_rejects_expired_token而不是test_case_03。若项目采用src/布局建议将产品代码作为已安装包导入并在项目配置中设置测试路径减少因当前工作目录不同导致的导入差异。4. assert 与失败信息直接使用 Python 的assert表达预期deftest_discounted_price():price100discount0.2final_priceprice*(1-discount)assertfinal_price80assertfinal_priceprice失败时 pytest 会重写断言表达式展示参与比较的值。测试应验证对使用者有意义的行为而非重复实现被测函数的内部算法。对浮点数优先使用容差比较assertactualpytest.approx(0.3)5. Fixture组织前置条件和清理Fixture 是 pytest 的依赖准备机制。测试函数把 fixture 名称写作参数pytest 会找到对应 fixture 并注入其返回值。Fixture 可以依赖其他 fixture形成可组合的准备流程。importpytestpytest.fixturedefuser_record():return{id:7,name:Lin,active:True}deftest_user_is_active(user_record):assertuser_record[active]isTrueFixture 的作用范围scope控制同一 fixture 实例复用的生命周期scope生命周期常见用途function默认每个测试函数隔离性强的临时数据class一个测试类类内共享且安全的状态module一个测试模块模块级昂贵准备package一个包包内共享资源session整次 pytest 会话只读客户端、公共静态资源作用范围越大创建次数越少但共享状态和测试耦合的风险越高。只有资源允许安全共享时才扩大范围。使用 yield 清理资源yield前准备资源yield后释放。即使测试断言失败pytest 也会执行已到达的清理部分pytest.fixturedeftemp_connection():connectionopen_connection()yieldconnection connection.close()对于多步资源初始化应让每一步初始化后就有对应清理保障避免后续步骤失败时留下半初始化资源。Fixture 应保持职责清晰避免构造执行大量隐式动作的“万能 fixture”。在conftest.py中定义的 fixture 可被其目录及子目录中的测试发现不需要显式导入。建议把共享 fixture 放在合理的目录层级避免顶层conftest.py成为难以理解的全局依赖集合。6. 参数化用一份测试覆盖多组数据当测试逻辑一致、输入不同时可用pytest.mark.parametrizeimportpytestpytest.mark.parametrize(text, expected,[(hello,5),(,0),(你好,2),],)deftest_character_count(text,expected):assertlen(text)expected每组参数会作为独立用例报告失败时能直接看到是哪组数据出错。对边界、非法输入、等价类尤其有用。给参数命名避免把所有组合塞进一个测试函数组合数量过多时按风险优先级取舍防止套件膨胀。也可参数化 fixture或者为单条参数指定pytest.param(..., marks...)。7. Marker给测试分类与筛选内置 marker 可表达常见执行条件importpytestpytest.mark.skip(reason功能尚未支持)deftest_future_feature():...pytest.mark.skipif(notis_windows(),reason仅 Windows 支持)deftest_windows_behavior():...pytest.mark.xfail(reason上游缺陷尚未修复)deftest_known_bug():...项目也可以定义自有标记例如slow、integration然后按标记筛选pytest -m not slow。自定义标记应在配置文件中注册便于团队理解其意图并避免拼写错误。标记是分类和选择机制不应把长期失败测试无限期xfail或skip掩盖起来。标注原因定期清理过期标记。8. 常用内置能力预期异常用pytest.raises验证异常类型必要时进一步检查异常消息deftest_invalid_age_is_rejected():withpytest.raises(ValueError,matchage must be positive):create_profile(age0)将操作放在with内部的最小范围避免其它代码意外触发同一个异常造成误通过。临时文件与目录tmp_pathfixture 为每个测试提供独立临时目录deftest_export_writes_json(tmp_path):outputtmp_path/report.jsonexport_report(output)assertoutput.exists()assertstatus: okinoutput.read_text(encodingutf-8)测试不必在仓库中创建、维护和清理临时文件。Monkeypatch内置monkeypatchfixture 可在测试期间临时替换属性、字典项、环境变量或工作目录并在测试结束后恢复deftest_reads_api_key_from_environment(monkeypatch):monkeypatch.setenv(API_KEY,test-key)assertload_api_key()test-key优先替换系统边界时间、网络、环境变量、文件系统入口避免过度模拟内部细节。9. 配置与命令行pytest 支持pyproject.toml、pytest.ini、tox.ini等配置形式。新项目可把相关设置集中在pyproject.toml[tool.pytest.ini_options] testpaths [tests] addopts -ra markers [ slow: 运行时间较长的测试, integration: 需要外部服务或多个组件的测试, ]常用命令命令用途pytest运行发现到的测试pytest -q简洁输出pytest -v显示每个用例名称pytest -x第一次失败后停止pytest --maxfail2最多遇到指定失败数后停止pytest -k user and not slow按名称表达式筛选pytest -m integration按 marker 筛选pytest --collect-only只收集不执行pytest --durations10显示最慢的若干用例pytest --tbshort使用较短的回溯信息参数可以组合例如pytest -q -m not slow。CI 中建议保留清晰的失败信息-x适合快速反馈不一定适合作为唯一的完整回归运行方式。10. 推荐项目结构与实践my_project/ ├── pyproject.toml ├── src/ │ └── my_project/ │ └── calculator.py └── tests/ ├── conftest.py ├── unit/ │ └── test_calculator.py └── integration/ └── test_api.py实用原则先测可观察行为输入、输出、状态变化和错误处理比私有实现细节稳定。每个测试可独立运行避免依赖执行顺序或共享可变数据。失败要可诊断测试名描述场景断言定位明确测试数据具有代表性。管理外部依赖将网络、数据库、时间等边界隔离集成测试使用可控环境。合理分层快速单元测试、较慢集成测试分开组织并按需要选择运行集合。把运行方式自动化在持续集成中安装项目依赖并运行测试失败时保留日志。插件按需添加插件能扩展能力也会带来依赖和维护成本选择有明确价值的插件。11. 常见问题为什么没有发现测试检查文件名、函数名是否符合约定确认运行目录和配置中的testpaths然后执行pytest --collect-only -q查看收集结果。为什么本地能导入CI 却报模块找不到常见原因是本地工作目录或PYTHONPATH偶然提供了导入路径。将项目按规范安装到虚拟环境统一 CI 工作目录和安装步骤并检查src/布局及配置。为什么测试之间互相影响通常是共享可变状态、数据库记录未清理、临时文件重名、环境变量未恢复或测试依赖运行顺序。使用 function-scope fixture、独立数据标识和可靠清理来修复根因。单元测试还是端到端测试两者解决的问题不同。单元测试反馈快、定位明确集成或端到端测试验证更多真实组件的协作但运行成本和环境复杂度更高。应按风险分层组合而不是期待一种测试覆盖全部风险。12. 小结pytest 从简单的assert起步借助 Fixture 复用准备逻辑使用参数化覆盖输入空间再用标记和配置组织不同运行集合。框架提供结构与反馈可靠的测试仍来自独立、明确、贴近用户行为的验证。官方资料pytest 官方文档Get Startedpytest 官方文档Fixturespytest 官方文档参数化pytest 官方文档命令行用法pytest 官方文档配置选项
返回列表