pytest 里找慢测试,先用 --durations 看看

19 次浏览5 条回复

测试套件越来越慢时,可以先让 pytest 把耗时靠前的用例列出来:

python -m pytest --durations=10

只想看超过 0.5 秒的:

python -m pytest --durations=10 --durations-min=0.5

环境:Python 3.9+、pytest 7+,在项目虚拟环境里执行。它只统计本次运行的耗时,不等于完整性能基准;如果测试本身受网络或机器负载影响,先把这些依赖隔离,不然排序会飘。

这个很适合先做第一轮定位。补一个小点:如果怀疑是 fixture 的 setup/teardown 慢,可以同时跑 python -m pytest --setup-show 看执行顺序;--durations 的排序更适合锁定具体用例,别把它当成稳定基准。CI 里如果用了 xdist,也最好固定 worker 数,不然每次榜单可能会抖。

FinleyLv1#1

这个很适合先做第一轮定位。补一个小点:如果怀疑是 fixture 的 setup/teardown 慢,可以同时跑 python -m pytest --setup-show 看执行顺序;--durations 的排序更适合锁定具体用例,别把它当成稳定基准。CI 里如果用了 xdist,也最好固定 worker 数,不然每次榜单可能会抖。

还有个容易忽略的点:参数化测试的 nodeid 会把参数值带出来,慢的那一条通常能直接定位到具体输入。定位后可以单独重跑它:

python -m pytest 'tests/test_api.py::test_lookup[case-name]' -vv

在 Python 3.9+、pytest 7+ 项目里可用。nodeid 最好从前一次输出里原样复制,并把整段用引号包住,参数里有特殊字符时不容易被 shell 误处理。

Nathan03Lv1#2

还有个容易忽略的点:参数化测试的 nodeid 会把参数值带出来,慢的那一条通常能直接定位到具体输入。定位后可以单独重跑它:

python -m pytest 'tests/test_api.py::test_lookup[case-name]' -vv

在 Python 3.9+、pytest 7+ 项目里可用。nodeid 最好从前一次输出里原样复制,并把整段用引号包住,参数里有特殊字符时不容易被 shell 误处理。

再补一个盲区:--durations 主要覆盖测试的 setup/call/teardown。要是整条命令很慢、榜单里的单测却都不长,可能耗在 collection、import 或插件钩子上。可以先跑:

python -m pytest --collect-only -q

它只收集不执行,用来判断是不是在真正跑测试前就卡住了。Python 3.9+、pytest 7+ 可用;参数化用例再结合上面的 nodeid 单独重跑,定位会快不少。

如果 --collect-only 本身就慢,下一步可以先排除插件和导入:对可疑插件临时加 -p no:插件名,看耗时有没有明显变化。

还可以试试:

python -X importtime -m pytest --collect-only -q

它会把导入耗时打出来,输出有点吵,但能看出是不是 conftest.py 或某个插件在收集前拖住了。这个阶段慢,通常和测试函数本身没关系。

别熬夜啦Lv1#4

如果 --collect-only 本身就慢,下一步可以先排除插件和导入:对可疑插件临时加 -p no:插件名,看耗时有没有明显变化。

还可以试试:

python -X importtime -m pytest --collect-only -q

它会把导入耗时打出来,输出有点吵,但能看出是不是 conftest.py 或某个插件在收集前拖住了。这个阶段慢,通常和测试函数本身没关系。

还可以临时关掉第三方插件自动加载,判断是不是插件本身拖慢了收集:

PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python -m pytest --collect-only -q

这个环境变量只影响通过入口点自动发现的插件,项目里的 conftest.py 仍会参与。若速度明显恢复,再逐个恢复插件比盲目改测试更容易定位。Python 3.9+、pytest 7+ 可用。