Python如何正确mock用于初始化类变量的函数,解决导入提前执行问题?
问题原因
你的猜测完全正确:类变量的赋值逻辑会在所属模块第一次被导入时就执行。你当前的测试代码在文件顶部就导入了ClassToTest,执行导入的节点some_func还没有被mock,ClassToTest.var已经被赋值为原函数的返回结果,后续再打补丁自然不会生效。
解决方案
方案1:补丁生效后再导入被测类
不要在测试文件顶部导入ClassToTest,将导入逻辑移到补丁已经生效的测试方法内部:
# test_code.py import unittest from unittest import mock def mock_function(): return {"someOtherKey": "someOtherValue"} class Test_Code(unittest.TestCase): @mock.patch('src.code.some_func', new=mock_function) def test_ClassToTest(self): # 补丁生效后再导入类,此时类变量初始化会调用mock后的函数 from src.code import ClassToTest self.assertEqual(ClassToTest.var, {"someOtherKey": "someOtherValue"})
方案2:重新加载模块触发类变量重新初始化
如果已经提前导入了模块,可以用importlib.reload在补丁生效后重新加载模块,类定义会重新执行,类变量也会重新赋值:
# test_code.py import unittest from unittest import mock import importlib # 仅导入模块,不提前初始化类 from src import code def mock_function(): return {"someOtherKey": "someOtherValue"} class Test_Code(unittest.TestCase): @mock.patch('src.code.some_func', new=mock_function) def test_ClassToTest(self): # 重新加载模块,触发类变量重新初始化 importlib.reload(code) self.assertEqual(code.ClassToTest.var, {"someOtherKey": "someOtherValue"})
注意:如果多个测试用例依赖该模块,重新加载可能会影响其他测试的上下文,建议测试执行完成后再次重载恢复原始状态,或者限定补丁的作用范围。
方案3:业务代码优化为懒加载(推荐)
如果允许调整业务代码,可以将类变量改为懒加载形式,避免模块导入阶段就执行带外部依赖的逻辑,后续mock也不需要调整导入顺序:
# code.py 调整后 def some_func(): # doing some connections and stuff return {'someKey': 'someVal'} class ClassToTest: _var = None @classmethod @property def var(cls): if cls._var is None: cls._var = some_func() return cls._var
调整后你原本的测试代码不需要修改导入逻辑也可以正常mock生效。
内容的提问来源于stack exchange,提问作者Vivek
相关产品推荐
相关产品推荐

