首页 > 解决方案 > 如何正确模拟实例化类变量的函数?

问题描述

我的源文件中有类似的东西

# code.py

def some_func():
    # doing some connections and stuff
    return {'someKey': 'someVal'}


class ClassToTest:

    var = some_func()

我的测试文件看起来像这样......我试图模拟some_func,因为我想避免创建连接。

# test_code.py

from src.code import ClassToTest


def mock_function():
    return {"someOtherKey": "someOtherValue"}


class Test_Code(unittest.TestCase):

    @mock.patch('src.code.some_func', new=mock_function)
    def test_ClassToTest(self):
        self.assertEqual(ClassToTest.var, {"someOtherKey": "someOtherValue"})

但这不起作用。另一方面,如果var是即时变量模拟工作正常。我猜这是由于类变量在导入期间被初始化。在初始化some_func之前如何正确模拟?var

标签: pythonpython-3.xmockingpython-unittestclass-variables

解决方案


导入code.py的时候还没有激活补丁,所以ClassToTest.var初始化的时候使用的是原来的some_func. 只有这样补丁src.code.some_func才能生效,现在显然为时已晚。

解决方案 1

您可以做的是修补some_func然后重新加载code.py,以便它重新初始化ClassToTest包括其属性var。因此,由于我们在重新加载时已经有一个活动补丁code.py,因此ClassToTest.var将使用补丁值进行设置。

  • 但是如果类和补丁函数都在同一个文件中,我们就不能这样做,所以为了使它可测试移动some_func到另一个文件,然后只导入它。

src/code.py

from src.other import some_func


class ClassToTest:
    var = some_func()

源/其他.py

def some_func():
    # doing some connections and stuff
    return {'realKey': 'realValue'}

测试代码.py

from importlib import reload
import sys
import unittest
from unittest import mock

from src.code import ClassToTest  # This will always refer to the unpatched version


def mock_function():
    return {"someOtherKey": "someOtherValue"}


class Test_Code(unittest.TestCase):
    def test_real_first(self):
        self.assertEqual(ClassToTest.var, {"realKey": "realValue"})

    @mock.patch('src.other.some_func', new=mock_function)
    def test_mock_then_reload(self):
        # Option 1:
        # import src
        # reload(src.code)

        # Option 2
        reload(sys.modules['src.code'])

        from src.code import ClassToTest  # This will be the patched version
        self.assertEqual(ClassToTest.var, {"someOtherKey": "someOtherValue"})

    def test_real_last(self):
        self.assertEqual(ClassToTest.var, {"realKey": "realValue"})

输出

$ pytest -q 
...                                                                                           [100%]
3 passed in 0.04s

解决方案 2

如果您不希望some_func在测试期间调用真实数据,那么仅重新加载是不够的。需要做的是永远不要导入包含该文件的文件,ClassToTest也不要导入任何会间接导入它的文件。仅在已建立活动补丁some_func后才导入它。

from importlib import reload
import sys
import unittest
from unittest import mock

# from src.code import ClassToTest  # Remove this import!!!


def mock_function():
    return {"someOtherKey": "someOtherValue"}


class Test_Code(unittest.TestCase):
    @mock.patch('src.other.some_func', new=mock_function)
    def test_mock_then_reload(self):
        from src.code import ClassToTest  # Move the import here once the patch has taken effect already
        self.assertEqual(ClassToTest.var, {"someOtherKey": "someOtherValue"})

推荐阅读