【问题标题】:Assert a sequence of calls to different mock objects断言对不同模拟对象的一系列调用
【发布时间】:2014-12-05 00:32:21
【问题描述】:

如何使用 Python mock 库来断言对不同模拟对象的特定调用序列?

比如我要断言:

  • 拨打foo(spam, eggs);那么
  • 拨打bar(beans, ham);那么
  • foo(sausage) 的电话。

我可以修补foobar 中的每一个,并且生成的每个模拟对象都允许我对对该模拟的调用进行断言。但是我需要访问整个调用的序列来对该序列进行断言。

是的,理想情况下,我只需要检查结果状态并在事后对其进行断言。但这对于某些系统是不可行的,正确状态的唯一可行描述是“这些调用是按特定顺序进行的”。

我可以使用mock 库的哪些功能来访问对不同对象的一系列调用,并断言调用按正确的顺序按预期进行?

【问题讨论】:

    标签: python unit-testing python-mock


    【解决方案1】:

    Mock 实际上提供了类似这样的内置功能。模拟经常有一个父模拟......例如

    somemock.foo  # parent is somemock
    

    父级没有直接暴露在mock API中,但是对子级的调用是在父级上注册的。

    import mock
    m = mock.Mock()
    m.a('hello world')
    m.b('goodbye world')
    m.c('kittens!')
    m.a('Howdy')
    m.assert_has_calls([
      mock.call.a('hello world'),
      mock.call.b('goodbye world'),
      mock.call.c('kittens!'),
      mock.call.a('Howdy')
    ])  # passes silently
    m.assert_has_calls([
      mock.call.a('hello world'),
      mock.call.b('goodbye world'),
      mock.call.a('Howdy'),
      mock.call.c('kittens!')
    ]) # Error
    # Traceback (most recent call last):
    #   File "<stdin>", line 1, in <module>
    #   File "/usr/local/lib/python2.7/dist-packages/mock.py", line 863, in assert_has_calls
    #     'Actual: %r' % (calls, self.mock_calls)
    # AssertionError: Calls not found.
    # Expected: [call.a('hello world'), call.b('goodbye world'), call.a('Howdy'), call.c('kittens!')]
    # Actual: [call.a('hello world'),
    #  call.b('goodbye world'),
    #  call.c('kittens!'),
    #  call.a('Howdy')]
    

    但是,您可能会说“我的模拟并不都来自同一个父级”。一切还没有丢失!您可以创建一个父对象并在事后将它们附加到它。

    parent_mock = mock.Mock()
    parent_mock.attach_mock(foomock, 'foo')
    parent_mock.attach_mock(barmock, 'bar')
    

    现在你可以做我们上面做的同样的断言(只要你不需要保留原始模拟的父母......那么我不知道该告诉你什么......)

    【讨论】:

      【解决方案2】:

      解决此问题的初步尝试是使用专门的Mock 子类,它将调用注册到提供的序列对象中,该对象可以是您喜欢的任何共享序列。

      from copy import deepcopy
      import mock
      
      class CallRegisterMock(mock.MagicMock):
          """ A mock object that registers each call. """
      
          def __init__(self, call_register, *args, **kwargs):
              super(CallRegisterMock, self).__init__(*args, **kwargs)
      
              self.call_register = call_register
      
          def __call__(self, *args, **kwargs):
              args = deepcopy(args)
              kwargs = deepcopy(kwargs)
              call = mock.call(*args, **kwargs)
              qualified_call = (self, call)
              self.call_register.append(qualified_call)
              super(CallRegisterMock, self).__call__(*args, **kwargs)
      

      这有缺点:

      • 它可能正在重新发明一个或多个轮子。 (如果您认为是,请添加一个更好的答案。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-26
        • 2011-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-12
        相关资源
        最近更新 更多