我想要的是:确保在with
语句中创建的所有Foo
实例都通过wraps=Foo.foo
将其foo
实例方法包装在MagicMock中。我希望这样做的原因是,我可以在方法foo
上跟踪所创建的所有Foo
实例的call_count
。现在我这么说似乎是不可能的.
>>> from mock import patch
...
... class Foo(object):
...
... def foo(self):
... return "foo"
...
... with patch("__main__.Foo.foo", wraps=Foo.foo) as m:
... foo = Foo()
... print(foo.foo())
Traceback (most recent call last):
File "a.py", line 12, in <module>
print(foo.foo())
File "/disk/software/lib/python27/mock/mock.py", line 1062, in __call__
return _mock_self._mock_call(*args, **kwargs)
File "/disk/software/lib/python27/mock/mock.py", line 1132, in _mock_call
return self._mock_wraps(*args, **kwargs)
TypeError: unbound method foo() must be called with Foo instance as first argument (got nothing instead)
问题模拟的foo
方法没有绑定到通过foo = Foo()
创建的foo
实例,因为它包装了未绑定的方法Foo.foo
。有人知道如何确保被模拟的方法绑定到实例吗?
我已经知道的:
>>> foo = Foo()
... with patch.object(foo, "foo", wraps=foo.foo) as m:
... print(foo.foo())
"foo"
但这并不能满足我的约束,即对象必须在patch
上下文中实例化。
发布于 2018-08-04 05:01:46
我上面提出的不正确的解决方案的问题
with patch("__main__.Foo.foo", wraps=Foo.foo) as m:
...
Foo
上的foo
方法被模拟为包装未绑定的方法Foo.foo
,这自然不起作用,因为未绑定的方法Foo.foo
在以后调用时不知道它附加到了哪个实例。
我能想到的最简单的解决方案
from mock import patch, MagicMock
class Foo:
def foo(self):
return "foo"
class MockFoo(Foo):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Every instance of MockFoo will now have its `foo` method
# wrapped in a MagicMock
self.foo = MagicMock(wraps=self.foo)
with patch("__main__.Foo", MockFoo) as m:
foo = Foo()
print(foo.foo())
assert foo.foo.call_count == 1
发布于 2021-04-28 18:27:55
这对于Python mock来说是如此令人讨厌,以至于我最终构建了一个自定义的补丁实现(如果需要的话,可以扩展其他特性)。
import contextlib
class Patcher:
UNCHANGED_RET = object()
def __init__(self):
self.call_count = 0
self.return_value = Patcher.UNCHANGED_RET
@contextlib.contextmanager
def patch(klass, method_name):
patcher = Patcher()
orig_method = getattr(klass, method_name)
def new_method(myself, *args, **kwargs):
patcher.call_count += 1
orig_return_value = orig_method(myself, *args, **kwargs)
if patcher.return_value != Patcher.UNCHANGED_RET:
return patcher.return_value
return orig_return_value
setattr(klass, method_name, new_method)
yield patcher
setattr(klass, method_name, orig_method)
使用方法如下:
class MyClass:
def f(self):
return 42
x = MyClass()
with patch(MyClass, 'f') as f_patcher:
y = MyClass() # inside or outside -- does not matter
assert x.f() == 42
assert f_patcher.call_count == 1
f_patcher.return_value = 7
assert y.f() == 7
assert f_patcher.call_count == 2
https://stackoverflow.com/questions/44768483
复制相似问题