如何打印pytest fixtures的依赖关系图
pytest fixture依赖关系图构建方法
pytest支持重定义上层作用域中定义的fixture,且这类重定义会沿fixture依赖树向上传递,示例如下:
# tests/conftest.py @pytest.fixture() def foo(): ... @pytest.fixture() def bar(): ... @pytest.fixture() def baz(foo, bar): ... @pytest.fixture() def fizz(foo, baz, capsys): ... # tests/deep/down/test_something.py @pytest.fixture def foo(): return "something completely different" def test_something(fizz, other, fixtures): # 对该测试而言,`foo`在fizz的整个依赖树中都会被替换为本地重定义的版本 # 即fizz、baz都会接收到这个重写后的foo实例 @pytest.mark.parameterize("foo", ["a", "b", "c"]) def test_something_else(fizz, even, more, fixtures): # 参数化重定义foo的逻辑同上
这是一项灵活性极强的特性,但当fixture依赖链路较长、重定义场景较多时,很难追踪完整的依赖关系,日常开发中经常需要解答这类问题:
“从fixture foo到fizz的依赖路径是怎样的?”“当前测试依赖的fixture中,哪些会受到foo重定义的影响?”
大家熟知的pytest --fixtures tests_folder/命令只会打印指定测试目录下的所有可用fixture列表,不会展示fixture之间的依赖关系,没法直接得到类似如下结构的清晰依赖图:
foo bar ^ ^ ^ | \ / | baz buzz | ^ ^ | /------/ fizz
可落地的实现方案
自定义插件解析依赖
pytest内部在测试收集阶段就已经完成了所有fixture的依赖解析,你可以通过写一个极简的pytest插件来提取完整依赖关系:
- 在pytest配置阶段拿到fixture管理器实例:
session._fixturemanager,里面存储了所有作用域下的fixture定义 - 对每个fixture定义,读取它的
argnames属性,就能拿到该fixture直接依赖的所有fixture名称 - 递归遍历依赖列表即可构建完整的依赖树,遍历过程中注意遵循pytest的fixture查找优先级:优先取离当前测试用例最近作用域的fixture定义,就能正确处理上层fixture被重定义的场景
- 拿到依赖树结构后,既可以打印成ASCII字符图,也可以导出为通用图结构格式生成可视化图片。
如果只是临时排查单个测试用例的fixture执行顺序和依赖,也可以直接加--setup-show参数运行测试,它会按执行顺序列出所有触发的fixture,只是不会整理成清晰的树状结构。
使用现成插件
目前已有成熟的pytest插件可以直接生成fixture依赖图,安装后执行对应命令即可直接输出ASCII格式依赖树,或者导出可视化的依赖关系图,自动处理fixture重定义的覆盖逻辑,不需要自己编写解析代码。
内容的提问来源于stack exchange,提问作者decorator-factory
相关产品推荐
相关产品推荐

