首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Python模拟:包装实例方法

Python模拟:包装实例方法
EN

Stack Overflow用户
提问于 2017-06-27 04:51:51
回答 2查看 3.6K关注 0票数 8

我想要的是:确保在with语句中创建的所有Foo实例都通过wraps=Foo.foo将其foo实例方法包装在MagicMock中。我希望这样做的原因是,我可以在方法foo上跟踪所创建的所有Foo实例的call_count。现在我这么说似乎是不可能的.

代码语言:javascript
复制
>>> 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。有人知道如何确保被模拟的方法绑定到实例吗?

我已经知道的:

代码语言:javascript
复制
>>> foo = Foo()
... with patch.object(foo, "foo", wraps=foo.foo) as m:
...     print(foo.foo())
"foo"

但这并不能满足我的约束,即对象必须在patch上下文中实例化。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-08-04 05:01:46

我上面提出的不正确的解决方案的问题

代码语言:javascript
复制
with patch("__main__.Foo.foo", wraps=Foo.foo) as m:
    ...

Foo上的foo方法被模拟为包装未绑定的方法Foo.foo,这自然不起作用,因为未绑定的方法Foo.foo在以后调用时不知道它附加到了哪个实例。

我能想到的最简单的解决方案

代码语言:javascript
复制
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
票数 7
EN

Stack Overflow用户

发布于 2021-04-28 18:27:55

这对于Python mock来说是如此令人讨厌,以至于我最终构建了一个自定义的补丁实现(如果需要的话,可以扩展其他特性)。

代码语言:javascript
复制
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)

使用方法如下:

代码语言:javascript
复制
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
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44768483

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档