ARTICLE DETAIL

资讯详情

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

10分钟跑通你的第一个pytest测试:Python测试框架手把手教程

10分钟跑通你的第一个pytest测试:Python测试框架手把手教程 10分钟跑通你的第一个pytest测试Python测试框架手把手教程【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest改完一行折扣逻辑提交后用户反馈账单金额不对。你手动在控制台里重跑几个输入找原因却容易漏掉别的分支。pytest测试框架解决的就是这个问题把你关心的输入 → 期望输出写成一个个函数改完代码后一条命令全部重跑并精确告诉你哪一行错了。本教程会帮你用10分钟跑通第一个测试再学会断言、夹具、参数化三招最后为项目配上pytest.ini让它长期稳定运行。 跑通第一个测试装好pytest并读懂运行输出确认 Python 3.10 以上后一条命令装好 pytest 测试框架pip install pytest写一个贴近真实业务的最小用例。这里用一个折扣函数代替玩具例子测试文件和被测函数可以放在同一个文件里快速验证# test_pricing.py def discount(price: float, coupon: float) - float: return max(price - coupon, 0.0) # 优惠券不能把金额扣成负数 def test_coupon_deducts_from_price(): assert discount(100.0, 20.0) 80.0 # 正常抵扣 def test_coupon_cannot_go_negative(): assert discount(15.0, 20.0) 0.0 # 金额下限为 0在项目根目录敲pytest -v-v表示逐条列出每个用例的结果你会看到 test session starts platform linux -- Python 3.12, pytest-9.0.x, pluggy-1.6 -- /home/you/.venv/bin/python cachedir: .pytest_cache rootdir: /home/you/checkout collecting ... collected 2 items tests/test_pricing.py::test_coupon_deducts_from_price PASSED [ 50%] tests/test_pricing.py::test_coupon_cannot_go_negative PASSED [100%] 2 passed in 0.03s 前三行是环境信息platform显示 Python 和 pytest 版本rootdir是 pytest 定位到的项目根目录配置文件所在处。collected 2 items表示它按文件名和函数名收集到了 2 个用例——文件要叫test_*.py函数要叫test_*这是收集规则。最后一行的passed in 0.03s是总结论全部通过耗时 0.03 秒。 看懂pytest内部结构一张表讲清6个核心模块pytest 的所有核心代码都在src/_pytest/下。一次运行大致按这个顺序流转pytest命令行入口 └─ config 解析参数、加载 pytest.ini、定位 rootdir └─ collect 扫描 test_*.py收集出一个个用例 └─ runner 逐个执行 ├─ fixtures 用例运行前准备、运行后清理 ├─ assertion 改写 assert失败时给出详细对比 └─ terminal 把结果渲染成上面的终端报告对应到真实目录主要模块如下路径均相对仓库根目录模块职责真实路径config解析命令行参数、加载配置、定位项目根src/_pytest/config/fixtures夹具系统依赖注入与生命周期管理src/_pytest/fixtures.pyassertion断言重写失败时生成可读的对比信息src/_pytest/assertion/markpytest.mark标记与按标记筛选src/_pytest/mark/runner执行每个测试项并收集结果src/_pytest/runner.pyterminal渲染.、PASSED等终端报告src/_pytest/terminal.py你平时只和命令行交互但这张表解释了为什么-m筛选生效为什么失败信息这么详细——分别对应 mark 和 assertion 两个模块。✍️ 写好用例的三个进阶招式断言、夹具、参数化招式一靠断言重写读懂失败现场一句话价值pytest 会在收集阶段改写你的assert失败时自动打印两边实际值不用你手写打印。把折扣函数的max删掉模拟一个 bug重新运行tests/test_pricing.py:9: in test_coupon_cannot_go_negative assert discount(15.0, 20.0) 0.0 E assert -5.0 0.0E行直接告诉你函数返回了-5.0而不是期望的0.0定位不到问题就不用猜。涉及浮点数时改用近似比较from pytest import approx assert 0.1 0.2 approx(0.3) # 浮点误差范围内视为相等什么时候该用所有断言场景。普通适合整数、字符串、结构体浮点运算、金额计算一律approx。改写机制的实现在 src/_pytest/assertion/。招式二用夹具替代手写setup/teardown一句话价值夹具fixture让你把准备测试数据的代码从每个用例里抽出来按函数名注入。import pytest pytest.fixture def invoice(): # 夹具名就是测试函数的参数名 return {items: [keyboard, mouse], amount: 499.0} def test_invoice_amount_positive(invoice): # 参数名对上即自动注入 assert invoice[amount] 0夹具还支持作用域pytest.fixture(scopesession)表示整个会话只初始化一次适合数据库连接这类昂贵的资源。什么时候该用同一个用例要准备超过 3 行数据或者多个用例共享同一份数据时。实现入口在 src/_pytest/fixtures.py。招式三用参数化消灭复制粘贴一句话价值同一个断言逻辑要验证多组输入时用pytest.mark.parametrize写一次、跑 N 遍。import pytest pytest.mark.parametrize( (price, coupon), [(100.0, 20.0), (15.0, 20.0), (9.9, 5.0)], # 每组数据独立跑一遍下面的函数 ) def test_discount_never_negative(price, coupon): assert discount(price, coupon) 0.0运行后报告会列出 3 条用例哪组数据挂了一目了然。什么时候该用边界值测试0、负数、上限、多版本兼容、不同配置的对比。只要发现自己在复制测试函数只改数字就该参数化。 让测试跑稳pytest.ini配置与报告命令用pytest.ini固定项目默认行为在项目根目录建pytest.ini团队所有人跑pytest时自动带上这些设定[pytest] addopts -v --tbshort # 默认逐条显示失败信息用短格式 testpaths tests # 不传路径时默认扫 tests 目录 pythonpath src # 把 src 加入导入路径 markers slow: 慢速用例用 -m not slow 跳过配合目录组织项目保持这种结构即可myproject/ ├── src/myproject/ # 源码包 ├── tests/ # 测试文件名 test_*.py │ └── test_pricing.py └── pytest.ini用命令行控制每次怎么跑pytest -m not slow # 只跳过标记为 slow 的用例其余照跑 pytest -x # 第一个失败就停下适合逐条修复 pytest --lf # 只重跑上次失败的那些依赖 .pytest_cache pytest --junitxmlreport.xml # 输出 XML 报告给 CI 平台消费--lf依赖缓存模块 src/_pytest/cacheprovider.py第一次跑完会在.pytest_cache/里记录失败清单日常迭代用它最省时间。⚠️ 新手最容易踩的几个坑5个高频问题Q为什么报collected 0 items一个测试都没跑收集规则是死约定文件必须以test_开头、函数必须以test_开头且不能带多余参数test_foo(self)这种 unittest 写法在这里无效。改对命名后先跑pytest --collect-only确认收集到了再执行。Q运行报ModuleNotFoundError但代码里 import 明明没问题pytest 的 rootdir 和导入路径和你在 IDE 里点运行不是一回事。把源码放在src/下时在pytest.ini里加一行pythonpath src即可或者用pip install -e .把项目装进当前环境。Q两个目录里各有一个同名test_helper.py运行直接报错这是导入冲突非包目录里的同名文件会撞车。给目录加__init__.py组成包或者把测试文件重命名成全局唯一。详细的导入机制见 doc/en/explanation/pythonpath.rst。Q夹具明明写了用例却提示找不到对应参数注入靠参数名精确匹配pytest.fixture的函数名必须和用例参数名完全一致。另外夹具定义在别的测试文件里时不共享公共夹具要放到tests/conftest.py中。Q改了代码重跑太慢怎么只跑刚才失败的那批pytest --lflast-failed。注意它只认缓存里的记录一次全部通过后缓存清空--lf会退化成全量运行这属于正常行为。pytest 用文件即用例、断言即检查、夹具即环境三件事覆盖了大部分日常测试需求。下一步可以看 doc/en/how-to/writing_plugins.rst 写自己的插件、研究testing/plugins_integration/里与 Django 等框架的集成方式、或用pytest --benchmark类插件给热点函数做性能基准。【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表