通常,我关心被测系统对软件的另一部分(我在测试中模拟)进行的确切调用,而不关心这些调用发生的顺序。(例如,因为模拟替换的真实其他部分的最终效果不取决于这些调用的顺序。)
换句话说,我希望我的测试
- 如果没有进行所有预期的调用,则失败
- 如果进行了意外调用,则失败(因此
unittest.mock.Mock.assert_has_calls
还不够) - 如果只有调用的顺序发生变化,则不会失败
- 如果拨打电话的频率低于或高于预期,则失败
所以,我必须检查模拟对象的mock_calls
属性。我可以使用 PyHamcrest 以一种通用且合理理解的方式做到这一点contains_inanyorder
:
#!/usr/bin/env python3
from unittest import TestCase, main
from unittest.mock import Mock, call
from hamcrest import assert_that, contains_inanyorder as contains_in_any_order
class TestMockCalls(TestCase):
def test_multiple_calls(self):
m = Mock()
m('foo')
m.bar('baz')
m('foo')
assert_that(
m.mock_calls, contains_in_any_order(
call('foo'),
call('foo'),
call.bar('baz'),
)
)
if __name__ == '__main__':
main()
这适用于通过测试,例如上面的测试:
$> ./test_mock_calls.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
当它应该失败时它也会失败(如上所述,例如当您将其中一个更改为时m('foo')
)m('F00')
,但在这种情况下的输出并不像它可能的那样有用:
$> ./test_mock_calls.py
F
======================================================================
FAIL: test_multiple_calls (__main__.TestMockCalls)
----------------------------------------------------------------------
Traceback (most recent call last):
File "./test_mock_calls.py", line 16, in test_multiple_calls
call.bar('bay'),
AssertionError:
Expected: a sequence over [, , ] in any order
but: not matched:
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
我可以从中收集的唯一信息(除了哪个测试和哪个断言失败)是总共预期有多少次模拟调用(通过计算方括号之间的逗号),但不是预期的调用,更多重要的是,实际观察到了哪些电话以及多少电话。
这是unittest.mock
PyHamcrest 中的错误还是我使用错误?