仅对类的所有实例的一个属性进行Mock
我有这三个模块:
# 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)
start
和 stop
方法基本上是对 __enter__
和 __exit__
方法的包装,这样你就可以手动“模拟”一个使用相同补丁的 with
语句。