调用从“spec”创建的模拟的模拟方法的不存在方法时引发错误

Raise error when calling a non-existent method of a mocked method of a mock created from `spec`

from unittest import mock
class A:
    def f(self): pass
m = mock.MagicMock(spec_set=A)
m.f.called_once()  # I want this to fail
Out[42]: <MagicMock name='mock.f.called_once()' id='140326790593792'>

我在单元测试中犯了一个错误,在模拟方法上调用了 called_once 而不是 assert_called_once。调用会产生一个新的 MagicMock 实例,因此测试通过了,但没有检查我打算检查的内容——如果调用了该方法。当从 spec_set 创建 mock 时,如果未定义方法,是否有办法使 mock 失败?就像我希望 spec_set 一直应用到方法模拟本身。

使用create_autospec:

from unittest import mock
class A:
    def f(self): pass
m = mock.create_autospec(A)
m.f()
m.f.assert_called_once()  # works OK
m.f.misstyped_called_once() # raises
Traceback (most recent call last):
  File "/Users/ant/opt/miniconda3/envs/deeplearning/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3437, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-85-8fe8ab09b722>", line 7, in <module>
    m.f.misstyped_called_once() # raises
  File "/Users/ant/opt/miniconda3/envs/deeplearning/lib/python3.8/unittest/mock.py", line 637, in __getattr__
    raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'misstyped_called_once'

原来文档中有一整节专门讨论这个主题:https://docs.python.org/3/library/unittest.mock.html#autospeccing

Autospeccing is based on the existing spec feature of mock. It limits the api of mocks to the api of an original object (the spec), but it is recursive (implemented lazily) so that attributes of mocks only have the same api as the attributes of the spec.

编辑

看起来有一个 school of thought 避免使用 assert_called_xxx 支持显式

assert mock_restart.call_count == 1
assert mock_restart.call_args == mock.call(“some argument”)

another school 提出了一种方法论解决方案——如果你做 TDD,那么你的测试 必须 在你投入任何实现之前失败,这可以防止错误正面。