pytest疑问:内部对象更新后,快捷Fixture未同步是否属不良实践
问题:pytest fixture快捷方式未同步对象是否属于不良实践?
我编写了如下pytest代码,其中fixture foo是fixture myclass内部foo对象的快捷方式,仅为简化调用无需编写myclass.foo。但调用myclass.update_foo()更新内部foo后,foo fixture并未同步,尽管更新前二者指向同一对象。请问这是否属于应避免的不良实践?
class MyClass: def __init__(self): self.foo = object() def update_foo(self): self.foo = object() @pytest.fixture def myclass(): yield MyClass() @pytest.fixture def foo(myclass): return myclass.foo def test_foo(myclass, foo): assert myclass.foo is foo myclass.update_foo() assert myclass.foo is foo # <-- 此处断言失败
回答
首先明确断言失败的原因:foo fixture在初始化时返回的是myclass.foo当时指向的对象引用。当myclass.update_foo()执行时,它是给self.foo重新赋值了一个全新的对象,而foo变量依然持有原来的对象引用,自然不会同步更新。
这种写法属于容易引发混淆的不良实践,建议尽量避免,原因如下:
- 违背直观预期:使用者通常会默认
foofixture始终对应myclass.foo的当前值,但实际上它只是静态的初始引用,很容易写出错误的断言或业务逻辑。 - 降低代码可读性:其他维护者看到
foofixture时,未必能立刻意识到它只是初始对象的引用,后续对象更新后出现的不一致会大幅增加调试成本。
如果想要简化调用,有两种更安全的改进方向:
- 直接使用
myclass.foo:放弃foofixture,虽然多写几个字符,但逻辑完全清晰,不会出现引用不一致的问题,这是最稳妥的方案。 - 用动态代理/getter替代静态引用:如果一定要保留快捷方式,可以让
foofixture返回一个能动态获取当前myclass.foo的结构,比如:
@pytest.fixture def foo(myclass): # 用属性代理动态获取最新的foo class FooProxy: @property def current(self): return myclass.foo yield FooProxy() # 测试中调用方式 def test_foo(myclass, foo): assert myclass.foo is foo.current myclass.update_foo() assert myclass.foo is foo.current # 断言通过
或者更简洁的用lambda(注意调用时要加括号):
@pytest.fixture def foo(myclass): yield lambda: myclass.foo # 测试调用 def test_foo(myclass, foo): assert myclass.foo is foo() myclass.update_foo() assert myclass.foo is foo()
总的来说,这种静态引用的快捷方式看似简化代码,实则引入了隐藏的不一致风险,除非有特殊场景需求,否则优先选择直接访问myclass.foo来保证代码的清晰性和可维护性。
内容的提问来源于stack exchange,提问作者jjj
相关产品推荐
相关产品推荐

