如何在pytest测试函数内初始化测试对象,而非参数化/收集阶段?
解决方案
核心思路是把参数化的预计算结果换成延迟执行的工厂函数,让初始化逻辑只在测试用例运行阶段执行,而非收集阶段。
步骤1:修改参数化列表
将原来直接调用fun*()的地方,替换为返回对应函数调用的lambda(或偏函数),避免在收集阶段执行初始化:
import pytest from functools import partial from mymodule import * @pytest.mark.parametrize( "arg_factory", [ lambda: fun1(), lambda: fun2(), ] + [ lambda n=n: fun3(n) for n in range(10) ] + [ lambda n=n, model=model: fun4(n, model) for n in range(3, 7) for model in ["explicit", "implicit"] ], )
注意:列表推导式里的lambda必须用n=n、model=model绑定当前循环变量,否则所有lambda会引用循环最后一轮的变量值(闭包陷阱)。如果觉得lambda写法麻烦,也可以用functools.partial,比如partial(fun3, n),效果完全一致。
步骤2:在测试函数内执行初始化
测试函数里调用传入的工厂函数,得到实际需要的参数,再执行原测试逻辑:
def test_foobar(arg_factory): # 此时才执行初始化,耗时操作会在测试运行阶段触发 arg = arg_factory() # ... 原来的测试逻辑,用arg代替原参数即可 pass
额外优化:给用例命名(可选)
如果想在测试报告里更清晰看到每个用例对应哪个函数调用,可以给每个参数化项加上id:
@pytest.mark.parametrize( "arg_factory", [ pytest.param(lambda: fun1(), id="fun1"), pytest.param(lambda: fun2(), id="fun2"), ] + [ pytest.param(lambda n=n: fun3(n), id=f"fun3({n})") for n in range(10) ] + [ pytest.param(lambda n=n, model=model: fun4(n, model), id=f"fun4({n}, {model})") for n in range(3, 7) for model in ["explicit", "implicit"] ], )
这样运行测试时,就能明确看到每个耗时的用例对应的是哪个fun*调用,方便定位问题。
内容的提问来源于stack exchange,提问作者Nico Schlömer
相关产品推荐
相关产品推荐

