我正在玩补丁和模拟,并试图了解类方法补丁和修补类方法补丁之间的区别。在这两种情况下,我都创建了类实例并尝试通过使用显式指令的有界方法来修补绑定方法autospec=True
。但是,在第一种情况下,我成功地修补了具有预期行为的方法,而在第二种情况下,修补后的对象表现出意外。
它演示了以下代码:
from mock import patch
class B(object):
def zero(self):
return 0
class A(object):
def __init__(self):
self.y = B()
self.i = self.y.zero()
def mocked_zero(*args, **kwargs):
return 1
a = A()
assert a.i == 0 # expected
with patch('__main__.B.zero', autospec=True) as mocked_B_zero:
mocked_B_zero.side_effect = mocked_zero
a = A()
assert a.i == 1 # expected
mocked_B_zero.assert_called_once_with(a.y) # expected
with patch('__main__.B', autospec=True) as mocked_B:
instance = mocked_B.return_value
instance.zero.side_effect = mocked_zero
# I placed the following line but it changes nothing
instance.zero.autospec = True
a = A()
assert a.i == 1 # expected
instance.zero.assert_called_once_with() # why does it pass?
我预计该指令autospec=True
会完成这项工作并且instance.zero.assert_called_once_with(a.y)
会通过。
应该注意的是指令autospec=True
改变了mocked_b
对象。
with patch('__main__.B') as mocked_B:
创建一个对象mocked_B
:<MagicMock name='B' id='...'>
。然而
with patch('__main__.B', autospec=True) as mocked_B:
创建一个不同的对象mocked_B
:<MagicMock name='B' spec='B' id='...'>
autospec=True
在修补类方法的情况下,这是对关键字参数含义的一个很好的解释:
Mock 的 autospec 将错误的参数注入到被调用的函数中
https://www.toptal.com/python/an-introduction-to-mocking-in-python (见评论)
但autospec=True
在类修补的情况下,该指令的含义仍不清楚。
所以,我有几个问题:
autospec=True
在整个类修补的情况下,指令意味着什么?- 为什么
instance.zero.assert_called_once_with()
在修补类的方法修补的情况下通过? - 是否可以在第二种情况下通过有界方法修补绑定方法以便
instance.zero.assert_called_once_with(a.y)
通过?
更新
出现需要修补类及其方法之一的情况。
我需要测试一个类,比如说A
,它在其实现中使用另一个类,比如说B
。简化后看起来像这样:
class B(object):
def __init__(self, config):
pass
def get_items(self): # instead of 'zero' function
while True: # infinite loop
item = self.request_to_db(some_params)
yield item
class A(object):
def __init__(self, config)
self.b = B(config)
def run(self):
for item in self.b.get_items():
do_some_action(item)
我需要
- 检查是否使用参数调用了
A
像类一样a = A(config)
的实例化类。B
config
- 检查调用
a.run()
方法 get_items 是否被调用并放入循环中。
为此我想要
B
用一些模拟对象修补整个类get_items
用一些特殊构造的模拟对象(此处使用 side_effect 选项)修补方法,StopIteration
在定义的迭代次数后引发异常。
当然,do_some_action
方法也是打补丁的。