如何模拟类属性,以便访问该属性调用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应该解析为函数,而不是函数将返回的值。