我有一个类(ClassToBeMocked),它在另一个类(ContainerClass)的init中实例化。 我想模拟实例化的 ClassToBeMocked 并替换其 dunder call 方法。
我的代码适用于常规方法,但不适用于 call dunder 方法。
from unittest.mock import patch, Mock, MagicMock
class ClassToBeMocked:
def __call__(self, x):
return 42
def regular_method(self, x):
return 42
class ContainerClass:
def __init__(self):
self.runner = ClassToBeMocked()
def run_a(self):
return self.runner(x="x")
def run_b(self):
return self.runner.regular_method(x="x")
with patch("__main__.ClassToBeMocked") as mock:
instance = mock.return_value
instance.__call__ = MagicMock(return_value="Hey")
instance.regular_method = MagicMock(return_value="Hey")
test = ContainerClass()
print(test.run_a())
print(test.run_b())
此输出:
<MagicMock name='ClassToBeMocked()()' id='140033866116784'>
Hey
虽然我想拥有
Hey
Hey
在下面的示例中,
instance_cls()
不会调用 instance_cls.__call__
。它调用 type(instance_cls).__call__(instance_cls)
,因为 python 实际上调用类上定义的 __call__
方法,而不是实例(有关更多信息,请检查堆栈溢出上的 answer。
>>> class ClassToBeMocked:
... def __call__(self):
... return 42
...
>>> instance_cls = ClassToBeMocked()
>>> instance_cls()
... 42
关于您的示例,只需将
instance.__call__ = MagicMock(return_value="Hey")
更改为 instance.__class__.__call__ = MagicMock(return_value="Hey")
就可以了。
完整代码如下:
from unittest.mock import patch, Mock, MagicMock
class ClassToBeMocked:
def __call__(self, x):
return 42
def regular_method(self, x):
return 42
class ContainerClass:
def __init__(self):
self.runner = ClassToBeMocked()
def run_a(self):
return self.runner(x="x")
def run_b(self):
return self.runner.regular_method(x="x")
with patch("__main__.ClassToBeMocked") as mock:
instance = mock.return_value
instance.__class__.__call__ = MagicMock(return_value="Hey")
instance.regular_method = MagicMock(return_value="Hey")
test = ContainerClass()
print(test.run_a()) # print "Hey"
print(test.run_b()) # print "Hey"