gpt4 book ai didi

python-3.x - Python,模拟和包装方法而不实例化对象

转载 作者:行者123 更新时间:2023-12-02 23:27:25 28 4
gpt4 key购买 nike

我想模拟类的方法并使用wraps,以便实际调用它,但我可以检查传递给它的参数。我在几个地方(例如 here)看到,通常的方法如下(经过调整以表明我的观点):

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)

但是,这会实例化类 Potato,以便将模拟绑定(bind)到实例方法 spud.foo

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

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)

这当然行不通。我收到错误:

TypeError: foo() missing 1 required positional argument: 'self'

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

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 请求,并且我的框架实例化了一些类并使用它们的方法来满足请求。我想捕获发送到这些方法的参数,以便在我的测试中对它们进行一些断言

最佳答案

简而言之,您无法单独使用 Mock 实例来完成此操作。

patch.object 为指定实例(Potato)创建 Mock,即用单个 Mock 替换 Potato.foo被叫的那一刻。因此,无法将实例传递给 Mock,因为模拟是在任何实例之前创建的。据我所知,在运行时将实例信息获取到 Mock 也非常困难。

举例说明:

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')

输出为:

Created Mock instance a=(), kw={'name': 'foo', 'wrap': <function Potato.foo at 0x7f5d9bfddea0>}
no instances created
instance created

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

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 是有问题的。

关于python-3.x - Python,模拟和包装方法而不实例化对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44777408/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com