尝试模拟实例方法

时间:2017-08-09 07:50:46

标签: python recursion mocking

我想模拟一个实例方法。首先,我想修改参数,然后我想调用原始方法。

我试过了:

import mock

class Foo(object):
    def my_method(data):
        print(data)

def wrapped_method(data):
    return Foo.my_method(data.replace('x', 'o'))

with mock.patch.object(Foo, 'my_method', wraps=wrapped_method):
    foo = Foo()
    foo.my_method('axe')  # should print "aoe"

但我得到了这个例外:

/home/foo/bin/python /home/foo/src/wrap-instance-method.py
Traceback (most recent call last):
  File "/home/foo/src/wrap-instance-method.py", line 15, in <module>
    foo.my_method('axe')  # should print "aoe"
  File "/home/foo/local/lib/python2.7/site-packages/mock.py", line 955, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "/home/foo/local/lib/python2.7/site-packages/mock.py", line 1024, in _mock_call
    return self._mock_wraps(*args, **kwargs)
.....
    return self._mock_wraps(*args, **kwargs)
  File "/home/foo/src/wrap-instance-method.py", line 10, in wrapped_method
    return Foo.my_method(data.replace('x', 'o'))
  File "/home/foo/local/lib/python2.7/site-packages/mock.py", line 955, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "/home/foo/local/lib/python2.7/site-packages/mock.py", line 960, in _mock_call
    self.called = True
RuntimeError: maximum recursion depth exceeded while calling a Python object

Process finished with exit code 1

如何在没有递归异常的情况下调用原始方法?

2 个答案:

答案 0 :(得分:5)

您使用调用Foo.my_method的方法替换了Foo.my_method,所以是的,您将获得无限递归。

如果必须拥有原始方法,则需要在修改之前将其存储起来<​​/ p>:

def mock_method_factory(original):
    def wrapped_method(data):
        return original(data.replace('x', 'o'))
    return wrapped_method

with mock.patch.object(Foo, 'my_method', wraps=mock_method_factory(Foo.my_method)):
    # ...

但是,wraps无法处理绑定; original是未绑定的函数,wrapped_method不会传递self

mock库可能不是这里的最佳选择。你基本上做的是应用临时装饰。子类Foo或手动将装饰器应用于Foo.my_method

def mock_method_factory(original):
    def wrapped_method(self, data):
        return original(self, data.replace('x', 'o'))
    return wrapped_method

class MockedFoo(Foo):
    my_method = mock_method_factory(Foo.my_method)

foo = MockedFoo()
foo.my_method('axe')  # should print "aoe"

def mock_method_factory(original):
    def wrapped_method(self, data):
        return original(self, data.replace('x', 'o'))
    return wrapped_method

original = Foo.my_method
try:
    Foo.my_method = mock_method_factory(original)
    foo = Foo()
    foo.my_method('axe')  # should print "aoe"
finally:
    Foo.my_method = original

答案 1 :(得分:0)

我遇到了同样的问题。尝试了 Martijn 的解决方案,但遇到了自我问题。解决方法如下:

real_constructor = CLASS_NAME.__init__
def mock_constructor(self, **args):
    return real_constructor.(self=self, **args)

with mock.patch.object(Foo, '__init__', side_effect=mock_constructor):
    ...