仅对类的所有实例的一个属性进行Mock

0 投票
1 回答
17 浏览
提问于 2025-04-14 15:33

我有这三个模块:

# config.py
class Config:
    def __init__(self):
        self.some_attribute = "some value"  # <-- I want to mock this attribute
        self.another_attribute = 123
# some_module.py
from config import Config

def method_that_uses_config():
    print(Config().another_attribute)  # This should not be mocked
    return Config().some_attribute  # This should be mocked
# test_some_module.py:
from unittest.mock import patch

from some_module import method_that_uses_config

class TestConfig:
    @patch("some_module.Config")
    def test_method_that_uses_config(self, mock_config):
        mock_config.return_value.some_attribute = "mocked value"
        assert method_that_uses_config() == "mocked value"

现在这个代码只部分有效。Config类现在被完全模拟了,但我其实只想模拟一个特定的属性,其他的属性保持不变:

Config().some_attribute
# 'mocked value'
Config().another_attribute
# <MagicMock name='Config().another_attribute' id='4329847393'>

我希望Config().another_attribute能返回它原来的值(123)。我基本上想让Config实例的行为和正常一样,唯一的例外就是那个被模拟的属性。

我觉得这应该是个很基础的操作,但我可能漏掉了什么。

1 个回答

1

这里的内容是基于一个想法,就是使用一个共享的 Config 实例,而不是每次都去调用 Config 创建新的副本。为了简单起见,我们就直接用 Config 这个类本身作为那个实例,而不是去实例化它。

# config.py
class C:
    some_attribute = "some value"
    another_attribute = 123

# some_module.py
from config import Config

def method_that_uses_config():
    print(Config.another_attribute)
    return Config.some_attribute

在你的测试中,你可以使用 unittest.mock.patch.object 来替换你想要改变的那个属性的值。

# test_some_module.py:
from unittest.mock import patch

from some_module import method_that_uses_config

class TestConfig:
    @patch.object(some_module.Config, 'some_attribute', "mocked value")
    def test_method_that_uses_config(self, mock_config):
        assert method_that_uses_config() == "mocked value"

下面是一个简单的演示,展示了 patch.object 是怎么工作的。

>>> Config = class('Config', (), dict(a=1, b=2))
>>> p = patch.object(Config, 'a', 5)
>>> Config.a, Config.b
(1, 2)
>>> p.start()
5
>>> Config.a, Config.b
(5, 2)
>>> p.stop()
False
>>> Config.a, Config.b
(1, 2)

startstop 方法基本上是对 __enter____exit__ 方法的包装,这样你就可以手动“模拟”一个使用相同补丁的 with 语句。

撰写回答