You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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变量依然持有原来的对象引用,自然不会同步更新。

这种写法属于容易引发混淆的不良实践,建议尽量避免,原因如下:

  • 违背直观预期:使用者通常会默认foo fixture始终对应myclass.foo的当前值,但实际上它只是静态的初始引用,很容易写出错误的断言或业务逻辑。
  • 降低代码可读性:其他维护者看到foo fixture时,未必能立刻意识到它只是初始对象的引用,后续对象更新后出现的不一致会大幅增加调试成本。

如果想要简化调用,有两种更安全的改进方向:

  1. 直接使用myclass.foo:放弃foo fixture,虽然多写几个字符,但逻辑完全清晰,不会出现引用不一致的问题,这是最稳妥的方案。
  2. 用动态代理/getter替代静态引用:如果一定要保留快捷方式,可以让foo fixture返回一个能动态获取当前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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 09:44:57