首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Python,模拟和包装方法,不需要瞬时化对象

Python,模拟和包装方法,不需要瞬时化对象
EN

Stack Overflow用户
提问于 2017-06-27 18:00:31
回答 2查看 3.8K关注 0票数 19

我想模拟一个类的方法并使用wraps,这样它就会被实际调用,但我可以检查传递给它的参数。我在几个地方(例如here)看到,通常的方法如下(根据我的观点进行调整):

代码语言:javascript
复制
from unittest import TestCase
from unittest.mock import patch


class Potato(object):
    def foo(self, n):
        return self.bar(n)

    def bar(self, n):
        return n + 2


class PotatoTest(TestCase):
    spud = Potato()

    @patch.object(Potato, 'foo', wraps=spud.foo)
    def test_something(self, mock):
        forty_two = self.spud.foo(n=40)
        mock.assert_called_once_with(n=40)
        self.assertEqual(forty_two, 42)

但是,为了将模拟绑定到实例方法spud.foo,这将实例化类Potato

我需要的是在Potato的所有实例中模拟foo方法,并将它们包装在原始方法周围。也就是说,我需要以下内容:

代码语言:javascript
复制
from unittest import TestCase
from unittest.mock import patch


class Potato(object):
    def foo(self, n):
        return self.bar(n)

    def bar(self, n):
        return n + 2


class PotatoTest(TestCase):
    @patch.object(Potato, 'foo', wraps=Potato.foo)
    def test_something(self, mock):
        self.spud = Potato()
        forty_two = self.spud.foo(n=40)
        mock.assert_called_once_with(n=40)
        self.assertEqual(forty_two, 42)

这当然不起作用。我得到了错误:

代码语言:javascript
复制
TypeError: foo() missing 1 required positional argument: 'self'

但是,如果没有使用wraps,它就可以工作,所以问题不在于模拟本身,而在于它调用包装函数的方式。例如,这是可行的(但当然我必须“伪造”返回值,因为现在Potato.foo从未真正运行过):

代码语言:javascript
复制
from unittest import TestCase
from unittest.mock import patch


class Potato(object):
    def foo(self, n):
        return self.bar(n)

    def bar(self, n):
        return n + 2


class PotatoTest(TestCase):
    @patch.object(Potato, 'foo', return_value=42)#, wraps=Potato.foo)
    def test_something(self, mock):
        self.spud = Potato()
        forty_two = self.spud.foo(n=40)
        mock.assert_called_once_with(n=40)
        self.assertEqual(forty_two, 42)

这是有效的,但它不会运行原始函数,我需要运行该函数,因为返回值在其他地方使用(并且我不能在测试中伪造它)。

这是可以做到的吗?

注释我的需求背后的实际原因是我正在用webtest测试rest api。在测试中,我对一些路径执行了一些wsgi请求,我的框架实例化了一些类,并使用它们的方法来满足请求。我希望捕获发送给这些方法的参数,以便在测试中对它们执行一些asserts操作。

EN

回答 2

Stack Overflow用户

发布于 2018-12-19 11:22:18

简而言之,您不能仅使用Mock实例来做到这一点。

patch.object为指定的实例(土豆)创建Mock,也就是说,它在被调用的时候就用一个Mock替换了Potato.foo。因此,没有办法将实例传递给Mock,因为mock是在任何实例创建之前创建的。据我所知,在运行时将实例信息传递给Mock也是非常困难的。

举例说明:

代码语言:javascript
复制
from unittest.mock import MagicMock

class MyMock(MagicMock):
    def __init__(self, *a, **kw):
        super(MyMock, self).__init__(*a, **kw)
        print('Created Mock instance a={}, kw={}'.format(a,kw))

with patch.object(Potato, 'foo', new_callable=MyMock, wrap=Potato.foo):
    print('no instances created')
    spud = Potato()
    print('instance created')

输出为:

代码语言:javascript
复制
Created Mock instance a=(), kw={'name': 'foo', 'wrap': <function Potato.foo at 0x7f5d9bfddea0>}
no instances created
instance created

我建议对您的类进行修补,以便将Mock添加到正确的位置。

代码语言:javascript
复制
from unittest.mock import MagicMock

class PotatoTest(TestCase):
    def test_something(self):

        old_foo = Potato.foo
        try:
            mock = MagicMock(wraps=Potato.foo, return_value=42)
            Potato.foo = lambda *a,**kw: mock(*a, **kw)

            self.spud = Potato()
            forty_two = self.spud.foo(n=40)
            mock.assert_called_once_with(self.spud, n=40) # Now needs self instance
            self.assertEqual(forty_two, 42)
        finally:
            Potato.foo = old_foo

请注意,当您使用实例调用函数时,使用called_with是有问题的。

票数 5
EN

Stack Overflow用户

发布于 2018-07-14 04:31:46

您是否控制Potato实例的创建,或者至少在创建后可以访问这些实例?您应该这样做,否则您将无法检查特定的arg列表。如果是这样,您可以使用以下命令包装单个实例的方法

代码语言:javascript
复制
spud = dig_out_a_potato()
with mock.patch.object(spud, "foo", wraps=spud.foo) as mock_spud:
  # do your thing.
  mock_spud.assert_called...
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44777408

复制
相关文章

相似问题

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