如何使模拟类属性为每次访问调用一个函数



如何模拟类属性,以便访问该属性调用Python中的备用方法或函数?

我的问题与这个问题非常相似,但所有这些解决方案都会产生一个模拟属性,返回一个静态不变的值。

我想做一些类似的事情:

import unittest
from unittest import mock
from unittest.mock import PropertyMock
from faker import Faker

class MyClass:
@property
def text(self):
return 'blargh'

class Tests(unittest.TestCase):
@mock.patch('__main__.MyClass.text', new_callable=PropertyMock)
def test_mock_error(self, mock_text):        
#mock_text.return_value = Faker().text() # outputs the same text
mock_text.return_value = lambda: Faker().text() # outputs a function
print('a:', MyClass().text)
print('b:', MyClass().text)

if __name__ == '__main__':
unittest.main()

并且使CCD_ 1和CCD_。但目前mocked属性不调用lambda函数,而是将其模拟为。

如果我使用像这样的回调代理,我可以让它工作

@mock.patch('__main__.MyClass.text', new_callable=PropertyMock)
def test_mock_error(self, mock_text):        
from proxytypes3 import CallbackProxy
mock_text.return_value = CallbackProxy(lambda: Faker().text())
print('a:', MyClass().text)
print('b:', MyClass().text)

有没有一些方法可以使用vanilla-mock模块来实现这一点?

如果您只想返回一些固定的响应列表,@chepner的答案会起作用,即为mock的side_effect分配一些可迭代的内容。它也可以是无限可迭代的。

mock = Mock(side_effect=itertools.count())
mock()  # => 0
mock()  # => 1
mock()  # => 2

但是,如果您希望它实际调用一个函数,那么只需将该side_effect分配给一个可调用函数即可。

mock = Mock(side_effect=lambda: datetime.datetime.now())
(mock() - datetime.datetime.now()).total_seconds() < 0.0001  # True
(mock() - datetime.datetime.now()).total_seconds() < 0.0001  # True

为了完成这项工作,使用PropertyMock,让我们从文档中调整这个示例

>>> m = MagicMock()
>>> p = PropertyMock(side_effect=itertools.count())
>>> type(m).foo = p
>>> m.foo
0
>>> m.foo
1

对于像MyClass这样的非Mock类,您需要修补PropertyMock而不是setattr,b/c Mock有点特别:https://github.com/python/cpython/blob/fa118f0cd32e9b6cba68df10a176b502407243c8/Lib/unittest/mock.py#L405-L407

把所有这些放在一起,运行:

import unittest
from unittest.mock import PropertyMock, patch

class MyClass:
@property
def text(self):
return 'blargh'

state = ['a', 'b', 'c']

def stateful_func():
return state.pop()

class Tests(unittest.TestCase):
@patch('__main__.MyClass.text', new=PropertyMock(side_effect=stateful_func))
def test_mock_error(self):
print('a:', MyClass().text)
print('b:', MyClass().text)

if __name__ == '__main__':
unittest.main()

打印:

a: c
b: b
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK

使用side_effect提供要循环的值列表。

mock_test.side_effect = [Faker().text(), Faker.text()]

您的尝试从未调用函数;您明确表示a:0应该解析为函数,而不是函数将返回的值。

最新更新