为什么 mock.patch.multiple 的行为与 DEFAULT 不同?

Why does mock.patch.multiple behave different with DEFAULT?

如果 patch.multiple 用作 method/function 装饰器,则修补对象是否为 mock.DEFAULT 时其行为会有所不同。

示例:

from unittest import mock

class A: pass

@mock.patch.multiple('__main__', A=mock.DEFAULT)
def with_default(*args,**kwargs):
    if 'A' not in kwargs:
        print("with_default: A not passed")

@mock.patch.multiple('__main__', A=mock.Mock())
def with_other(*args, **kwargs):
    if 'A' not in kwargs:
        print("with_other: A not passed")

with_default() # nothing
with_other() # -> "with_other: A not passed"

我没有看到这种行为的原因吗?我看不出它在所有情况下都没有将新模拟传递给函数的原因。

docs 说(重点是我的):

Use DEFAULT as the value if you want patch.multiple() to create mocks for you. In this case the created mocks are passed into a decorated function by keyword, and a dictionary is returned when patch.multiple() is used as a context manager.

因此,当您不使用 DEFAULT 关键字时,您设置的修补方法 不会 传递给装饰函数。

您的 with_other 的装饰器案例可以重写为:

@mock.patch('__main__.A', new=mock.Mock())
def with_other():
    ... bla bla

即使您使用 new 属性,参数也不会传递给修饰函数,因为它假设您已经知道它并且您不需要将它作为参数获取。

一般来说:patch.multiple 可以用一堆简单的 patch 代替,其中路径由参数名称扩展并使用 new 设置值:

@patch.mutilple('foo', bar='bar', baz='baz')

成为

@patch('foo.baz', new='baz')
@patch('foo.bar', new='bar')

或通过 new 位置参数更简单:

@patch('foo.baz', 'baz')
@patch('foo.bar', 'bar')