Python中如何模拟类属性调用的函数(不直接mock类属性)?
如何模拟Python类属性中调用的函数(避免直接模拟类属性)
在Python中,当尝试模拟类属性初始化时调用的外部函数时,会遇到这样的问题:实例化对象时的模拟有效,但直接访问未实例化的类属性时模拟失效。具体场景如下:
代码示例
methods.py
def bar(): return "bar"
foo.py
from methods import bar class Foo: class_attribute = bar() def __init__(self): self.class_attribute = bar()
原测试代码(test_foo.py)
from foo import Foo import unittest from unittest.mock import patch class FooTestCase(unittest.TestCase): @patch("foo.bar") def test_mock_foo(self, mock): mock.return_value = "patched foo" print(Foo.class_attribute) # 输出 "bar" print(Foo().class_attribute) # 输出 "patched foo" unittest.main(argv=[''], verbosity=0,exit=False)
问题原因
Foo.class_attribute 在模块导入阶段就已经执行了 bar() 并完成赋值,此时测试中的 patch 还未生效,所以拿到的是原始的"bar";而实例化时的 self.class_attribute 是在 patch 生效后调用的 bar(),因此能拿到模拟值。
解决方案(不直接模拟类属性)
方案1:patch生效后重新加载模块
通过重新加载 foo 模块,让类的定义重新执行,此时 bar() 会使用已经被patch的版本,从而更新类属性的值。
修改后的test_foo.py:
import unittest from unittest.mock import patch import importlib import foo class FooTestCase(unittest.TestCase): @patch("foo.bar") def test_mock_foo(self, mock): mock.return_value = "patched foo" # 重新加载foo模块,触发类的重新定义 importlib.reload(foo) # 更新当前测试模块中的Foo引用 Foo = foo.Foo print(Foo.class_attribute) # 输出 "patched foo" print(Foo().class_attribute) # 输出 "patched foo" unittest.main(argv=[''], verbosity=0,exit=False)
方案2:延迟类属性的计算时机
修改 foo.py,将类属性的计算延迟到第一次访问时,确保调用 bar() 时 patch 已经生效。可以使用类级别的 property 实现:
修改后的foo.py:
from methods import bar class Foo: @classmethod def _get_class_attribute(cls): if not hasattr(cls, '_cached_class_attribute'): cls._cached_class_attribute = bar() return cls._cached_class_attribute # 将class_attribute定义为类属性的property class_attribute = property(_get_class_attribute) def __init__(self): self.class_attribute = bar()
此时原测试代码无需修改,运行后 Foo.class_attribute 会输出"patched foo"。
内容的提问来源于stack exchange,提问作者lisandro laurent
相关产品推荐
相关产品推荐

