我有这3个模块:
# 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 实例的行为与通常一样,唯一的例外是模拟属性。
我认为这应该是非常基本的,但我可能遗漏了一些东西。
这是基于更改为使用
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
语句。