【问题标题】:check unittest.mock call arguments agnostically w.r.t. whether they have been passed as positional arguments or keyword arguments不可知地检查 unittest.mock 调用参数 w.r.t.它们是作为位置参数还是关键字参数传递的
【发布时间】:2016-04-28 23:09:18
【问题描述】:

unittest.mock.Mock 对象被调用时,我可以使用调用的确切签名检查参数值:

from unittest.mock import Mock

m = Mock()  # creation of mock
m('foo', bar='baz')  # call to the mock
m.assert_called_once_with('foo', bar='baz')  # check call arguments

检查具有相同值的不同签名将失败。例如,如果我们检查 'baz' 作为位置参数而不是命名参数,则断言将失败:

m.assert_called_once_with('foo', 'baz')
# AssertionError: Expected call: mock('foo', 'baz')
# Actual call: mock('foo', bar='baz')

它必须。如果m替换的函数是

def actual_fu(foo, bar):
    # do something

那么调用将是等价的,但如果是的话

def a_different_actual_fu(foo, *args, bar='some default'):
    # do something

那么调用将不相等。 Mock 不知道实际函数的签名,所以它不能依赖我们在第一种情况下的等价性。

有没有一种方法可以通过让 Mock(或断言辅助函数或类似函数)知道实际函数被 mock 替换了吗?

Mock 对象可以使用可选的spec argumentautospeccing 来识别它所替换的对象(可以是函数或方法),但它们用于不同的目的(限制允许的调用在模拟上)并且不影响事后检查。

【问题讨论】:

  • AFAIK 在模拟 1.3 中是不可能的。但是......恕我直言,尝试理解呼叫语义不是模拟职责,而只是记录您如何进行呼叫。 assert_called_once_with(...) 只是方法所说的,而不是 assert_called_with_something_that_is_equivalent_to(...):听起来更像是存根的职责。无论如何,在某些情况下可能会非常有用....几天前我遇到了与此相关的事情stackoverflow.com/a/34811786/4101725

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


【解决方案1】:

可以使用可选的规范参数或autospeccing 使 Mock 对象知道它替换的对象(可以是函数或方法),但它们的用途不同..

这正是Issue 17015: mock could be smarter and inspect the spec's signature 改进问题的意义所在。 spec 实际上是非常相关的,现在使模拟函数签名感知。

看看当我们断言 mock 是使用关键字参数调用时,mock 是如何失败的 - 没有让它知道实际的函数签名:

>>> from unittest.mock import Mock
>>>
>>> def actual_fu(foo, bar):
...     pass
>>>
>>> m = Mock()
>>> m('foo', bar='baz')
<Mock name='mock()' id='4356741496'>
>>>
>>> m.assert_called_once_with(foo='foo', bar='baz')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/unittest/mock.py", line 803, in assert_called_once_with
    return self.assert_called_with(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/unittest/mock.py", line 792, in assert_called_with
    raise AssertionError(_error_message()) from cause
AssertionError: Expected call: mock(bar='baz', foo='foo')
Actual call: mock('foo', bar='baz')

现在,如果我们提供spec,看看一切如何:

>>> m = Mock(spec=actual_fu)
>>> m('foo', bar='baz')
<Mock name='mock()' id='4356249528'>
>>> 
>>> m.assert_called_once_with(foo='foo', bar='baz')
>>> m.assert_called_once_with('foo', bar='baz')
>>> m.assert_called_once_with(bar='baz', foo='foo')
>>> m.assert_called_once_with('foo', 'baz')
>>>

(使用 Python 3.5.1)

【讨论】:

  • 哦,哇。我实际上已经尝试过类似的方法,但可能在 Python 3.2 中它不起作用。刚刚用 Python 3.4 重新尝试过,它可以工作了! :-)
猜你喜欢
  • 2020-10-24
  • 2018-11-14
  • 2013-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 2017-12-27
  • 1970-01-01
相关资源
最近更新 更多