1

我正在玩补丁和模拟,并试图了解类方法补丁和修补类方法补丁之间的区别。在这两种情况下,我都创建了类实例并尝试通过使用显式指令的有界方法来修补绑定方法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在类修补的情况下,该指令的含义仍不清楚。

所以,我有几个问题:

  1. autospec=True在整个类修补的情况下,指令意味着什么?
  2. 为什么instance.zero.assert_called_once_with()在修补类的方法修补的情况下通过?
  3. 是否可以在第二种情况下通过有界方法修补绑定方法以便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)

我需要

  1. 检查是否使用参数调用了A像类一样a = A(config)的实例化类。Bconfig
  2. 检查调用a.run()方法 get_items 是否被调用并放入循环中。

为此我想要

  1. B用一些模拟对象修补整个类
  2. get_items用一些特殊构造的模拟对象(此处使用 side_effect 选项)修补方法,StopIteration在定义的迭代次数后引发异常。

当然,do_some_action方法也是打补丁的。

4

0 回答 0