为什么修补类的方法与修补类的修补方法的行为有所不同?

时间:2017-10-11 11:00:56

标签: python unit-testing mocking python-unittest.mock

我正在玩补丁和嘲笑,并试图了解其中的差别 在类的方法的补丁和补丁类的方法的补丁之间。 在这两种情况下,我都创建了类实例,并尝试通过有界的方法修补绑定的方法 显式指令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's autospec injects a wrong argument into a called function

https://www.toptal.com/python/an-introduction-to-mocking-in-python(见评论)

但是如果有的话,仍然不清楚指令autospec=True的含义 上课修补。

所以,我有几个问题:

  1. 指令autospec=True在整个课程修补时意味着什么?
  2. 为什么instance.zero.assert_called_once_with()如果通过instance.zero.assert_called_once_with(a.y) 修补课程的方法修补?
  3. 是否有可能在第二种情况下通过有界方法修补绑定方法,以便A通过?
  4. 更新

    需要修补类及其方法之一的情况。

    我需要测试一个类,比如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

    我需要

    1. 检查是否使用参数a = A(config)实例化了B类,configa.run()
    2. 检查调用B方法get_items是否被调用并放入循环中。
    3. 为此,我想要

      1. 用一些模拟对象修补整个类get_items
      2. 使用一些特殊构造的模拟对象修补方法StopIteration(此处使用side_effect选项) 在定义的迭代次数后提出do_some_action异常。
      3. 当然,onItemClicked方法也已修补。

0 个答案:

没有答案