首页 > 解决方案 > 提供无效参数时自动指定不抛出异常

问题描述

尝试模拟函数返回值,但 usingMock导致它在传递无效参数时不会引发异常:

In [1]: from unittest.mock import MagicMock, Mock, create_autospec

In [2]: def f(a):
   ...:   return 1
   ...: 
   ...: mf = Mock('f', autospec=True, return_value=2)
   ...: mf(None)
Out[2]: 2

In [3]: mf() # Why no error here?
Out[3]: 2

使用 create_autospec 执行此操作时,它确实会引发异常:

In [4]: mf2 = create_autospec(f)

In [5]: mf2(None)
Out[5]: <MagicMock name='mock()' id='140657928683232'>

In [6]: mf2()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-377889015999> in <module>
----> 1 mf2()

<string> in f(*args, **kwargs)

/usr/local/lib/python3.8/unittest/mock.py in checksig(*args, **kwargs)
    177     func, sig = result
    178     def checksig(*args, **kwargs):
--> 179         sig.bind(*args, **kwargs)
    180     _copy_func_details(func, checksig)
    181 

/usr/local/lib/python3.8/inspect.py in bind(self, *args, **kwargs)
   3023         if the passed arguments can not be bound.
   3024         """
-> 3025         return self._bind(args, kwargs)
   3026 
   3027     def bind_partial(self, /, *args, **kwargs):

/usr/local/lib/python3.8/inspect.py in _bind(self, args, kwargs, partial)
   2938                             msg = 'missing a required argument: {arg!r}'
   2939                             msg = msg.format(arg=param.name)
-> 2940                             raise TypeError(msg) from None
   2941             else:
   2942                 # We have a positional argument to process

TypeError: missing a required argument: 'a'

In [7]: 

标签: pythonmocking

解决方案


首先,您要创建Mock传递spec=f而不是spec='f'. 否则你给出一个字符串作为规范,而不是函数f

其次,模拟没有autospec论据。如果您阅读了文档的所有autospec部分,您会注意到autospec在使用patchor的上下文中总是提到这一点patch.object。这两个人autospec在他们的签名中。

这意味着您autospec在模拟中的创建被接受为kwargs模拟接受的其余部分的一部分。这些用于在模拟使用时配置模拟上的附加属性。

因此,当您的模拟只是使用一个 normalspec时,您可以确认它接受读取一个不应该存在的属性:

from unittest.mock import Mock

def f(a,b):
    return 1
 
mf = Mock(spec=f, autospec=True, return_value=2)
print(mf)
print(mf(None))
print(mf())
print(mf.autospec)

输出:

<Mock spec='function' id='139874223464352'>
2
2
True

因此,如果您编写如下代码,则 autospec 可以工作:

from unittest.mock import patch

def f(a):
    return 1

with patch("__main__.f", autospec=True, return_value=2) as mf:
    print(mf)
    print(mf(None))
    print(mf())

输出:

<function f at 0x7ff5a60a94c0>
2
Traceback (most recent call last):
... line 21, in <module>
    print(mf())
...
TypeError: missing a required argument: 'a'

推荐阅读