【问题标题】:Checking whether function has been called multiple times with different parameters检查函数是否已使用不同的参数多次调用
【发布时间】:2015-12-11 14:21:59
【问题描述】:

假设我们有一个函数 f(x,y) 和另一个函数

def g():
       # ...
       f(i,j) # i,j vary and f is called multiple times
       # ...

我们想编写一个单元测试来检查f 是否被调用的次数和参数是否正确。

def test_g():
      with patch('mymodule.f') as function:
          assert function.gacs.call_count == correct_no_calls

有

function.assert_called_with(...)

但这只是指最后一次通话。所以假设g 调用f(1,2) 然后f(2,3),function.assert_called_with(1,2) 是False。

还有

function.call_args_list

这会产生一个带有正确参数的call 对象列表。将此列表与我们在单元测试中创建的call 对象进行比较感觉像是一件非常讨厌的事情。 call 看起来像是模拟库的内部类。

有没有更好的方法来做到这一点?我使用这个设置来测试apply 函数的并行执行。

【问题讨论】:

    标签: python unit-testing testing mocking


    【解决方案1】:

    即使@MartinPieters 的回答也是正确的,我认为这不是最好的方法。模拟提供assert_has_calls 来完成这种职责。

    您的测试可能是:

    function.assert_has_calls([mock.call(1, 2), mock.call(2, 3)])
    

    mock.call 是对此类工作执行的辅助类。

    注意这是一个有调用,意思是调用列表应该在调用列表中并且不相等。为了解决它,我通常定义我自己的助手 assert_is_calls() 如下

    def assert_is_calls(m, calls, any_order=False):
       assert len(m.mock_calls) == len(calls)
       m.assert_has_calls(calls, any_order=any_order)
    

    那是一个简历示例

    >>> import mock
    >>> f = mock.Mock()
    >>> f(1)
    <Mock name='mock()' id='139836302999952'>
    >>> f(2)
    <Mock name='mock()' id='139836302999952'>
    >>> f.assert_has_calls([mock.call(1), mock.call(2)])
    >>> f.assert_has_calls([mock.call(2), mock.call(1)])
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/damico/.local/lib/python2.7/site-packages/mock/mock.py", line 969, in assert_has_calls
        ), cause)
      File "/home/damico/.local/lib/python2.7/site-packages/six.py", line 718, in raise_from
        raise value
    AssertionError: Calls not found.
    Expected: [call(2), call(1)]
    Actual: [call(1), call(2)]
    >>> f.assert_has_calls([mock.call(2), mock.call(1)], any_order=True)
    >>> f(3)
    <Mock name='mock()' id='139836302999952'>
    >>> f.assert_has_calls([mock.call(2), mock.call(1)], any_order=True)
    >>> f.assert_has_calls([mock.call(1), mock.call(2)])
    >>> assert len(f.mock_calls)==2
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AssertionError
    >>> assert len(f.mock_calls)==3
    >>> def assert_is_calls(m, calls, any_order=False):
    ...    assert len(m.mock_calls) == len(calls)
    ...    m.assert_has_calls(calls, any_order=any_order)
    ... 
    >>> assert_is_calls(f, [mock.call(1), mock.call(2), mock.call(3)])
    >>> assert_is_calls(f, [mock.call(1), mock.call(3), mock.call(2)])
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 3, in assert_is_calls
      File "/home/damico/.local/lib/python2.7/site-packages/mock/mock.py", line 969, in assert_has_calls
        ), cause)
      File "/home/damico/.local/lib/python2.7/site-packages/six.py", line 718, in raise_from
        raise value
    AssertionError: Calls not found.
    Expected: [call(1), call(3), call(2)]
    Actual: [call(1), call(2), call(3)]
    >>> assert_is_calls(f, [mock.call(1), mock.call(3), mock.call(2)], True)
    >>> assert_is_calls(f, [mock.call(1), mock.call(3)], True)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in assert_is_calls
    AssertionError
    >>> 
    

    【讨论】:

    • assert_has_calls 不关心 all 调用。你可以给它一个子集,它会返回 true。如果你想断言这些是 only 调用,那么你需要使用我的方法。
    • 你的包装函数,默认为any_order=False,实际上与assertEquals(m.mock_calls, calls)做同样的事情,那么为什么要额外的函数呢?该功能不会为您购买任何额外的东西..
    • @MartijnPieters 是的,这是因为我提到了我自己的助手assert_is_calls() 来关心所有呼叫……我现在正在阅读您的新评论:这都是关于命名的。它明确了你想要断言的内容。无论如何,您可以准确地选择您想要做的事情,检查订单或不检查,准确地检查所有呼叫或只是一个子集。
    • @MartijnPieters 只是一件事。 OP 并没有说他想检查 exactly 并以正确的顺序(或者我错过了什么?)。所以我涵盖了所有可能性。
    【解决方案2】:

    测试Mock().mock_calls list 是否等于您提供的mock.call() objects 列表:

    self.assertEquals(function.mock_calls, [
        mock.call(1, 2),
        mock.call(2, 3),
    ])
    

    这为您提供了精确的控制,需要匹配的顺序和调用次数。

    mock.call() 类不是内部的,它旨在用于此类断言。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-14
      • 2018-08-19
      • 2021-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多