pytest-picked只运行未提交的测试文件,因其通过git status --porcelain获取已修改的.py文件,再按路径/命名启发式匹配对应测试文件(如src/utils.py→tests/test_utils.py),不分析代码内容或函数级变更。

pytest-picked 为什么只跑未提交的测试文件?
它不是靠文件修改时间或 IDE 缓存判断,而是直接调用 git status --porcelain 获取当前工作区和暂存区中「已修改但未提交」的 Python 文件(含 *.py),再从中提取关联的测试文件——比如你改了 src/utils.py,它会去找 tests/test_utils.py 或 tests/utils/test_utils.py 这类命名/路径可映射的测试模块。
注意:它不分析函数级变更,也不扫描代码内容是否真影响了某个测试逻辑,纯路径+命名启发式匹配。所以如果测试文件名和被测模块完全无关(比如叫 tests/check_everything.py),即使你改了 src/core.py,它也不会运行这个测试。
安装与基础命令怎么写才不报错?
先确保已安装 pytest 和 pytest-picked:
pip install pytest pytest-picked
然后在 Git 仓库根目录下直接运行:
pytest --picked
常见失败点:
- 不在 Git 仓库里运行 → 报错
fatal: not a git repository,必须在.git目录所在层执行 - 有未跟踪的新文件(
?? new_file.py)→ 默认会被忽略,加--include-untracked才纳入检测 - 改了非 Python 文件(如
README.md)→ 不触发任何测试,因为插件只看*.py - 测试文件本身被修改但没提交 → 它也会被选中运行(哪怕只是改了注释)
如何让 pytest-picked 找到自定义测试路径?
默认它按 src/ → tests/、lib/ → test/ 这类常见映射猜测试位置。如果你的结构是 app/ + spec/,就得显式配置:
在项目根目录加 pytest.ini:
[tool:pytest] python_files = test_*.py *_test.py testpaths = spec picked_source_roots = app picked_test_roots = spec
关键配置项:
-
picked_source_roots:列出所有源码根目录(多个用空格分隔),插件从这里出发找改动文件 -
picked_test_roots:列出所有测试根目录,用于反向映射 -
testpaths告诉 pytest 去哪找测试,否则可能找不到spec/下的文件
改完配置后,pytest --picked 就能正确把 app/models/user.py 映射到 spec/models/test_user.py 了。
和 pytest-watch、git hooks 配合时要注意什么?
它本身不监听文件变化,所以不能直接替代 pytest-watch。但可以嵌入 pre-commit hook:
#!/bin/bash # .git/hooks/pre-commit if pytest --picked --quiet; then exit 0 else echo "❌ Some picked tests failed — aborting commit" exit 1 fi
陷阱:
- hook 中不要加
--exitfirst或-x,否则一个失败就跳过其余 picked 测试,误判覆盖率 - CI 环境通常没有本地未提交更改,
--picked会退化为「不运行任何测试」,务必在 CI 脚本里加判断:git status --porcelain | grep -q '\.py$' && pytest --picked || pytest tests/ - Windows 上
git status --porcelain换行符可能引发解析异常,建议在 CI 中统一用 Linux runner
实际效果取决于你的 Git 状态是否干净、路径命名是否规整、以及有没有跨根目录的耦合——这些地方一松动,picked 就容易漏测。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











