pytest加--durations参数可快速定位慢用例,无需改代码;默认显示最慢10个,可指定数量如--durations=1,单用例路径下精确显示其耗时,全局使用则显示所有用例中最慢的1个。

pytest命令行加--durations参数最直接
想快速知道哪个用例慢,不用改代码,直接在运行时加参数就行。默认只显示最慢的10个,但你可以指定数量:
-
pytest test_file.py::test_func_name --durations=1—— 只显示这个用例的耗时(注意:必须是单个用例路径,否则--durations可能被忽略) -
pytest --durations=1 -v—— 显示所有用例中耗时最长的1个,带详细名称 - 如果没看到耗时输出,大概率是因为用例执行太快(--durations-min=0.001强制显示毫秒级耗时
用pytest_runtest_makereport钩子精确捕获单个用例时间
命令行方式不够细(比如想存到日志、发告警、或做统计),就得插钩子。关键点不是“开始/结束打点”,而是利用pytest内置的call阶段时间戳:
- 在
conftest.py里定义pytest_runtest_makereport,检查report.when == "call"时取report.duration -
report.duration单位是秒,浮点数,精度足够(底层来自time.perf_counter()) - 别在
setup或teardown阶段读duration——那里的值是对应阶段耗时,不是整个用例 - 示例片段:
def pytest_runtest_makereport(item, call, report): if report.when == "call" and report.passed: print(f"{item.name} took {report.duration:.3f}s")
避免用time.time()手动包测试函数
有人会想到在测试函数里自己写start = time.time(),最后减一下。这看似简单,但实际埋雷:
- pytest的fixture(比如
tmpdir、capsys)和异常处理逻辑会影响真实耗时,手动计时不包含这些 - 如果用例失败(
assert抛异常),end = time.time()根本不会执行,导致报错+计时丢失 - 并发执行(
-n)下,多个线程共用同一个变量名容易覆盖,而pytest钩子天然按用例隔离 - 更隐蔽的问题:某些fixture(如数据库session)的teardown可能在
call之后才执行,手动计时会漏掉这部分
注意--tb=short等参数对耗时测量的干扰
调试参数本身不改变执行逻辑,但会影响输出行为,间接拖慢测量结果:
-
--tb=long或--fulltrace会在失败时生成大堆栈,尤其嵌套深时明显增加耗时 -
-s(禁用输出捕获)会让print实时刷屏,在CI环境可能因I/O阻塞引入抖动 - 真正做性能敏感测量时,建议加
--tb=no -q -s最小化干扰,并确保重复运行3次以上取中位数 - 如果发现同一用例多次运行耗时波动超过20%,先关掉所有第三方插件(比如
pytest-cov),它们常是隐形耗时大户
--durations;需要集成进流程或做阈值判断,就用钩子;手写计时器看着省事,但边界情况多,容易误判瓶颈在哪。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











