Python如何mock类级别参数依赖的外部导入变量
你的判断完全正确:Python 模块在首次导入时会执行所有顶层代码,Dummy 类的类变量 A_DICT 在测试文件顶部执行 from dummy import Dummy 时就已经用默认值 some_val=20 完成计算(此时值固定为 {'a':100, 'b':200}),后续对 some_val 的 mock 不会回溯修改已经生成的 A_DICT,因此测试不生效。
以下是三种可落地的解决方法:
方法1:直接mock类变量A_DICT(最简便,无需修改业务代码)
直接针对已经初始化完成的类变量做替换,不用绕去修改上游的some_val,适合仅测试场景需要调整值的情况:
import pytest from dummy import Dummy def test_some_func(mocker): mocker.patch.object(Dummy, 'A_DICT', {'a': 50, 'b': 100}) d = Dummy() assert d.some_func(5) == 250
方法2:调整导入/加载顺序,保证mock在模块初始化前完成
只要保证dummy模块执行初始化代码时,some_val已经被mock,类变量就会用到mock后的值。如果担心模块缓存导致重复导入不生效,可以搭配importlib.reload使用:
import pytest import importlib import dummy def test_some_func(mocker): mocker.patch('dummy.some_val', 10) # 重新加载模块,触发类变量重新初始化 importlib.reload(dummy) Dummy = dummy.Dummy d = Dummy() assert d.some_func(5) == 250
方法3:修改业务代码,将类变量改为懒加载(适合长期有动态取值需求的场景)
如果业务本身也有运行时修改some_val后同步更新A_DICT的需求,可以把类变量改成动态计算的形式,比如用@property装饰,每次访问时实时计算取值:
修改后的dummy.py:
from another_mod import some_val class Dummy: @property def A_DICT(self): return { 'a': some_val * 5, 'b': some_val * 10, } def some_func(self, x): return x * self.A_DICT['a']
这种实现下你原本的测试代码无需修改就能直接生效,因为每次访问A_DICT都会读取最新的some_val值。如果担心频繁生成字典的性能开销,可以额外加缓存逻辑,测试时主动清空缓存即可。
内容的提问来源于stack exchange,提问作者wstk
相关产品推荐
相关产品推荐

