unittest.mock --- 模拟对象库

3.3 版新加入.

原始碼:Lib/unittest/mock.py


unittest.mock 是一个用于测试的Python库。它允许使用模拟对象来替换受测系统的一些部分,并对这些部分如何被使用进行断言判断。

unittest.mock 提供的 Mock 类,能在整个测试套件中模拟大量的方法。创建后,就可以断言调用了哪些方法/属性及其参数。还可以以常规方式指定返回值并设置所需的属性。

此外,mock 还提供了用于修补测试范围内模块和类级别属性的 patch() 装饰器,和用于创建独特对象的 sentinel 。 阅读 quick guide 中的案例了解如何使用 MockMagicMockpatch()

mock 被设计为配合 unittest 使用且它是基于 'action -> assertion' 模式而非许多模拟框架所使用的 'record -> replay' 模式。

在 Python 的早期版本要单独使用 unittest.mock ,在 PyPI 获取 mock

快速上手

当您访问对象时, MockMagicMock 将创建所有属性和方法,并保存他们在使用时的细节。你可以通过配置,指定返回值或者限制可访问属性,然后断言他们如何被调用:

>>> from unittest.mock import MagicMock
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')

通过 side_effect 设置副作用(side effects) ,可以是一个 mock 被调用是抛出的异常:

>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
 ...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
...     return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)

Mock 还可以通过其他方法配置和控制其行为。例如 mock 可以通过设置 spec 参数来从一个对象中获取其规格(specification)。如果访问 mock 的属性或方法不在 spec 中,会报 AttributeError 错误。

使用 patch() 装饰去/上下文管理器,可以更方便地测试一个模块下的类或对象。你指定的对象会在测试过程中替换成 mock (或者其他对象),测试结束后恢复。

>>> from unittest.mock import patch
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... def test(MockClass1, MockClass2):
...     module.ClassName1()
...     module.ClassName2()
...     assert MockClass1 is module.ClassName1
...     assert MockClass2 is module.ClassName2
...     assert MockClass1.called
...     assert MockClass2.called
...
>>> test()

備註

当你嵌套 patch 装饰器时,mock 将以执行顺序传递给装饰器函数(Python 装饰器正常顺序)。由于从下至上,因此在上面的示例中,首先 mock 传入的 module.ClassName1

在查找对象的名称空间中修补对象使用 patch() 。使用起来很简单,阅读 在哪里打补丁 来快速上手。

patch() 也可以 with 语句中使用上下文管理。

>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
...     thing = ProductionClass()
...     thing.method(1, 2, 3)
...
>>> mock_method.assert_called_once_with(1, 2, 3)

还有一个 patch.dict() 用于在一定范围内设置字典中的值,并在测试结束时将字典恢复为其原始状态:

>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
...     assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original

Mock支持 Python 魔术方法 。使用模式方法最简单的方式是使用 MagicMock class. 。它可以做如下事情:

>>> mock = MagicMock()
>>> mock.__str__.return_value = 'foobarbaz'
>>> str(mock)
'foobarbaz'
>>> mock.__str__.assert_called_with()

Mock 能指定函数(或其他 Mock 实例)为魔术方法,它们将被适当地调用。 MagicMock 是预先创建了所有魔术方法(所有有用的方法) 的 Mock 。

下面是一个使用了普通 Mock 类的魔术方法的例子

>>> mock = Mock()
>>> mock.__str__ = Mock(return_value='wheeeeee')
>>> str(mock)
'wheeeeee'

使用 auto-speccing 可以保证测试中的模拟对象与要替换的对象具有相同的api 。在 patch 中可以通过 autospec 参数实现自动推断,或者使用 create_autospec() 函数。自动推断会创建一个与要替换对象相同的属相和方法的模拟对象,并且任何函数和方法(包括构造函数)都具有与真实对象相同的调用签名。

这么做是为了因确保不当地使用 mock 导致与生产代码相同的失败:

>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
...     pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
 ...
TypeError: <lambda>() takes exactly 3 arguments (1 given)

在类中使用 create_autospec() 时,会复制 __init__ 方法的签名,另外在可调用对象上使用时,会复制 __call__ 方法的签名。

Mock 类

Mock 是一个可以灵活的替换存根 (stubs) 的对象,可以测试所有代码。 Mock 是可调用的,在访问其属性时创建一个新的 mock 1 。访问相同的属性只会返回相同的 mock 。 Mock 会保存调用记录,可以通过断言获悉代码的调用。

MagicMockMock 的子类,它有所有预创建且可使用的魔术方法。在需要模拟不可调用对象时,可以使用 NonCallableMock  和 NonCallableMagicMock

patch() 装饰器使得用 Mock 对象临时替换特定模块中的类非常方便。 默认情况下 patch() 将为你创建一个 MagicMock。 你可以使用 patch()new_callable 参数指定替代 Mock 的类。

class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)

创建一个新的 Mock 对象。通过可选参数指定 Mock 对象的行为:

  • spec: 可以是要给字符串列表,也可以是充当模拟对象规范的现有对象(类或实例)。如果传入一个对象,则通过在该对象上调用 dir 来生成字符串列表(不支持的魔法属性和方法除外)。访问不在此列表中的任何属性都将触发 AttributeError

    如果 spec 是一个对象(而不是字符串列表),则 __class__ 返回 spec 对象的类。 这允许模拟程序通过 isinstance() 测试。

  • spec_setspec 的更严格的变体。如果使用了该属性,尝试模拟 setget 的属性不在 spec_set 所包含的对象中时,会抛出 AttributeError

  • side_effect :每当调用 Mock 时都会调用的函数。 参见 side_effect 属性。 对于引发异常或动态更改返回值很有用。 该函数使用与 mock 函数相同的参数调用,并且除非返回 DEFAULT ,否则该函数的返回值将用作返回值。

    另外, side_effect 可以是异常类或实例。 此时,调用模拟程序时将引发异常。

    如果 side_effect 是可迭代对象,则每次调用 mock 都将返回可迭代对象的下一个值。

    设置 side_effectNone 即可清空。

  • return_value :调用 mock 的返回值。 默认情况下,是一个新的Mock(在首次访问时创建)。 参见 return_value 属性 。

  • unsafe :默认情况下,访问任何名字以 assert, assret, asert, aseertassrt 开头的属性都将引发 AttributeError。 传入 unsafe=True 将允许访问这些属性。

    3.5 版新加入.

  • wraps :要包装的 mock 对象。 如果 wraps 不是 None ,那么调用 Mock 会将调用传递给 wraps 的对象(返回实际结果)。 对模拟的属性访问将返回一个 Mock 对象,该对象包装了 wraps 对象的相应属性(因此,尝试访问不存在的属性将引发 AttributeError )。

    如果明确指定 return_value ,则调用时不会返回包装对象,而是返回 return_value

  • name :mock 的名称。 在调试时很有用。 名称会传递到子 mock 。

还可以使用任意关键字参数来调用 mock 。 创建模拟后,将使用这些属性来设置 mock 的属性。 有关详细信息,请参见 configure_mock() 方法。

assert_called()

断言 mock 已被调用至少一次。

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called()

3.6 版新加入.

assert_called_once()

断言 mock 已被调用恰好一次。

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_once()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_once()
Traceback (most recent call last):
...
AssertionError: Expected 'method' to have been called once. Called 2 times.

3.6 版新加入.

assert_called_with(*args, **kwargs)

此方法是断言上次调用已以特定方式进行的一种便捷方法:

>>> mock = Mock()
>>> mock.method(1, 2, 3, test='wow')
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_with(1, 2, 3, test='wow')
assert_called_once_with(*args, **kwargs)

断言 mock 已被调用恰好一次,并且向该调用传入了指定的参数。

>>> mock = Mock(return_value=None)
>>> mock('foo', bar='baz')
>>> mock.assert_called_once_with('foo', bar='baz')
>>> mock('other', bar='values')
>>> mock.assert_called_once_with('other', bar='values')
Traceback (most recent call last):
  ...
AssertionError: Expected 'mock' to be called once. Called 2 times.
assert_any_call(*args, **kwargs)

断言 mock 已被调用并附带了指定的参数。

如果 mock 曾经 被调用过则断言通过,不同于 assert_called_with()assert_called_once_with() 那样只有在调用是最近的一次时才会通过,而对于 assert_called_once_with() 则它还必须是唯一的一次调用。

>>> mock = Mock(return_value=None)
>>> mock(1, 2, arg='thing')
>>> mock('some', 'thing', 'else')
>>> mock.assert_any_call(1, 2, arg='thing')
assert_has_calls(calls, any_order=False)

断言 mock 已附带指定的参数被调用。 将针对这些调用检查 mock_calls 列表。

如果 any_order 为假值则调用必须是顺序进行的。 在指定的调用之前或之后还可以有额外的调用。

如果 any_order 为真值则调用可以是任意顺序的,但它们都必须在 mock_calls 中出现。

>>> mock = Mock(return_value=None)
>>> mock(1)
>>> mock(2)
>>> mock(3)
>>> mock(4)
>>> calls = [call(2), call(3)]
>>> mock.assert_has_calls(calls)
>>> calls = [call(4), call(2), call(3)]
>>> mock.assert_has_calls(calls, any_order=True)
assert_not_called()

断言 mock 从未被调用过。

>>> m = Mock()
>>> m.hello.assert_not_called()
>>> obj = m.hello()
>>> m.hello.assert_not_called()
Traceback (most recent call last):
  ...
AssertionError: Expected 'hello' to not have been called. Called 1 times.

3.5 版新加入.

reset_mock(*, return_value=False, side_effect=False)

reset_mock 方法将在 mock 对象上重围所有的调用属性:

>>> mock = Mock(return_value=None)
>>> mock('hello')
>>> mock.called
True
>>> mock.reset_mock()
>>> mock.called
False

3.6 版更變: 向 reset_mock 函数添加了两个仅限关键字参数。

这在你想要创建一系列重用相同对象的断言时会很有用处。 请注意 reset_mock() 不会 清除返回值、side_effect 或任何你使用普通赋值所默认设置的子属性。 如果你想要重置 return_valueside_effect,则要为相应的形参传入 True。 子 mock 和返回值 mock (如果有的话) 也会被重置。

備註

return_valueside_effect 均为仅限关键字参数。

mock_add_spec(spec, spec_set=False)

为 mock 添加描述。 spec 可以是一个对象或字符串列表。 只有 spec 上的属性可以作为来自 mock 的属性被获取。

如果 spec_set 为真值则只有 spec 上的属性可以被设置。

attach_mock(mock, attribute)

附加一个 mock 作为这一个的属性,替换它的名称和上级。 对附加 mock 的调用将记录在这一个的 method_callsmock_calls 属性中。

configure_mock(**kwargs)

通过关键字参数在 mock 上设置属性。

属性加返回值和附带影响可以使用标准点号标记在子 mock 上设置并在方法调用中解包一个字典:

>>> mock = Mock()
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock.configure_mock(**attrs)
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

同样的操作可在对 mock 的构造器调用中达成:

>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

configure_mock() 的存在是使得 mock 被创建之后的配置更为容易。

__dir__()

Mock 对象会将 dir(some_mock) 的结果限制为有用结果。 对于带有 spec 的 mock 这还包括 mock 的所有被允许的属性。

请查看 FILTER_DIR 了解此过滤做了什么,以及如何停用它。

_get_child_mock(**kw)

创建针对属性和返回值的子 mock。 默认情况下子 mock 将为与其上级相同的类型。 Mock 的子类可能需要重载它来定制子 mock 的创建方式。

对于非可调用的 mock 将会使用可调用的变化形式(而非不是任意的自定义子类)。

called

一个表示 mock 对象是否已被调用的布尔值:

>>> mock = Mock(return_value=None)
>>> mock.called
False
>>> mock()
>>> mock.called
True
call_count

一个告诉你 mock 对象已被调用多少次的整数值:

>>> mock = Mock(return_value=None)
>>> mock.call_count
0
>>> mock()
>>> mock()
>>> mock.call_count
2
return_value

设置这个来配置通过调用该 mock 所返回的值:

>>> mock = Mock()
>>> mock.return_value = 'fish'
>>> mock()
'fish'

默认的返回值是一个 mock 对象并且你可以通过正常方式来配置它:

>>> mock = Mock()
>>> mock.return_value.attribute = sentinel.Attribute
>>> mock.return_value()
<Mock name='mock()()' id='...'>
>>> mock.return_value.assert_called_with()

return_value 也可以在构造器中设置:

>>> mock = Mock(return_value=3)
>>> mock.return_value
3
>>> mock()
3
side_effect

这可以是当该This can either be a function to be called when the mock 被调用时将被调用的一个函数,可调用对象或者要被引发的异常(类或实例)。

如果你传入一个函数则它将附带与该 mock 相同的参数被调用并且除了该函数返回 DEFAULT 单例的情况以外对该 mock 的调用都将随后返回该函数所返回的任何东西。 如果该函数返回 DEFAULT 则该 mock 将返回其正常值 (来自 return_value)。

如果你传入一个可迭代对象,它会被用来获取一个在每次调用时必须产生一个值的迭代器。 这个值可以是一个要被引发的异常实例,或是一个要从该调用返回给 mock 的值 (DEFAULT 处理与函数的情况一致)。

一个引发异常(来测试 API 的异常处理)的 mock 的示例:

>>> mock = Mock()
>>> mock.side_effect = Exception('Boom!')
>>> mock()
Traceback (most recent call last):
  ...
Exception: Boom!

使用 side_effect 来返回包含多个值的序列:

>>> mock = Mock()
>>> mock.side_effect = [3, 2, 1]
>>> mock(), mock(), mock()
(3, 2, 1)

使用一个可调用对象:

>>> mock = Mock(return_value=3)
>>> def side_effect(*args, **kwargs):
...     return DEFAULT
...
>>> mock.side_effect = side_effect
>>> mock()
3

side_effect 可以在构造器中设置。 下面是在 mock 被调用时增加一个该属性值并返回它的例子:

>>> side_effect = lambda value: value + 1
>>> mock = Mock(side_effect=side_effect)
>>> mock(3)
4
>>> mock(-8)
-7

side_effect 设为 None 可以清除它:

>>> m = Mock(side_effect=KeyError, return_value=3)
>>> m()
Traceback (most recent call last):
 ...
KeyError
>>> m.side_effect = None
>>> m()
3
call_args

这可以是 None (如果 mock 没有被调用),或是 mock 最近一次被调用时附带的参数。 这将采用元组的形式:第一个成员也可以通过 args 特征属性来访问,它是 mock 被调用时所附带的任何位置参数 (或为空元组),而第二个成员也可以通过 kwargs 特征属性来访问,它则是任何关键字参数 (或为空字典)。

>>> mock = Mock(return_value=None)
>>> print(mock.call_args)
None
>>> mock()
>>> mock.call_args
call()
>>> mock.call_args == ()
True
>>> mock(3, 4)
>>> mock.call_args
call(3, 4)
>>> mock.call_args == ((3, 4),)
True
>>> mock.call_args.args
(3, 4)
>>> mock.call_args.kwargs
{}
>>> mock(3, 4, 5, key='fish', next='w00t!')
>>> mock.call_args
call(3, 4, 5, key='fish', next='w00t!')
>>> mock.call_args.args
(3, 4, 5)
>>> mock.call_args.kwargs
{'key': 'fish', 'next': 'w00t!'}

call_args,以及列表 call_args_list, method_callsmock_calls 的成员都是 call 对象。 这些对象属性元组,因此它们可被解包以获得单独的参数并创建更复杂的断言。 参见 作为元组的 call

3.8 版更變: 新增 argskwargs 特性。

call_args_list

这是一个已排序的对 mock 对象的所有调用的列表(因此该列表的长度就是它已被调用的次数)。 在执行任何调用之前它将是一个空列表。 call 对象可以被用来方便地构造调用列表以与 call_args_list 相比较。

>>> mock = Mock(return_value=None)
>>> mock()
>>> mock(3, 4)
>>> mock(key='fish', next='w00t!')
>>> mock.call_args_list
[call(), call(3, 4), call(key='fish', next='w00t!')]
>>> expected = [(), ((3, 4),), ({'key': 'fish', 'next': 'w00t!'},)]
>>> mock.call_args_list == expected
True

call_args_list 的成员均为 call 对象。 它们可作为元组被解包以获得单个参数。 参见 作为元组的 call

method_calls

除了会追踪对其自身的调用,mock 还会追踪对方法和属性,以及 它们的 方法和属性的访问:

>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.property.method.attribute()
<Mock name='mock.property.method.attribute()' id='...'>
>>> mock.method_calls
[call.method(), call.property.method.attribute()]

method_calls 的成员均为 call 对象。 它们可以作为元组被解包以获得单个参数。 参见 作为元组的 call

mock_calls

mock_calls 会记录 所有 对 mock 对象、它的方法、魔术方法的调用 以及 返回值的 mock。

>>> mock = MagicMock()
>>> result = mock(1, 2, 3)
>>> mock.first(a=3)
<MagicMock name='mock.first()' id='...'>
>>> mock.second()
<MagicMock name='mock.second()' id='...'>
>>> int(mock)
1
>>> result(1)
<MagicMock name='mock()()' id='...'>
>>> expected = [call(1, 2, 3), call.first(a=3), call.second(),
... call.__int__(), call()(1)]
>>> mock.mock_calls == expected
True

mock_calls 的成员均为 call 对象。 它们可以作为元组被解包以获得单个参数。 参见 作为元组的 call

備註

mock_calls 的记录方式意味着在进行嵌套调用时,之前调用的形参不会被记录因而这样的比较将总是相等:

>>> mock = MagicMock()
>>> mock.top(a=3).bottom()
<MagicMock name='mock.top().bottom()' id='...'>
>>> mock.mock_calls
[call.top(a=3), call.top().bottom()]
>>> mock.mock_calls[-1] == call.top(a=-1).bottom()
True
__class__

通常一个对象的 __class__ 属性将返回其类型。 对于具有 spec 的 mock 对象来说,__class__ 将改为返回 spec 类。 这将允许 mock 对象为它们所替换 / 屏蔽的对象跳过 isinstance() 测试:

>>> mock = Mock(spec=3)
>>> isinstance(mock, int)
True

__class__ 是可以被赋值的,这将允许 mock 跳过 isinstance() 检测而不强制要求你使用 spec:

>>> mock = Mock()
>>> mock.__class__ = dict
>>> isinstance(mock, dict)
True
class unittest.mock.NonCallableMock(spec=None, wraps=None, name=None, spec_set=None, **kwargs)

不可调用的 Mock 版本。 其构造器的形参具有与 Mock 相同的含义,区别在于 return_valueside_effect 在不可调用的 mock 上没有意义。

使用类或实例作为 specspec_set 的 mock 对象能够跳过 isinstance() 测试:

>>> mock = Mock(spec=SomeClass)
>>> isinstance(mock, SomeClass)
True
>>> mock = Mock(spec_set=SomeClass())
>>> isinstance(mock, SomeClass)
True

Mock 类具有对 mock 操作魔术方法的支持。 请参阅 魔术方法 了解完整细节。

mock 操作类和 patch() 装饰器都接受任意关键字参数用于配置。 对于 patch() 装饰器来说关键字参数会被传给所创建 mock 的构造器。 这些关键字被用于配置 mock 的属性:

>>> m = MagicMock(attribute=3, other='fish')
>>> m.attribute
3
>>> m.other
'fish'

子 mock 的返回值和附带效果也可使用带点号的标记通过相同的方式来设置。 由于你无法直接在调用中使用带点号的名称因此你需要创建一个字典并使用 ** 来解包它:

>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

使用 spec (或 spec_set) 创建的可调用 mock 将在匹配调用与 mock 时内省规格说明对象的签名。 因此,它可以匹配实际调用的参数而不必关心它们是按位置还是按名称传入的:

>>> def f(a, b, c): pass
...
>>> mock = Mock(spec=f)
>>> mock(1, 2, c=3)
<Mock name='mock()' id='140161580456576'>
>>> mock.assert_called_with(1, 2, 3)
>>> mock.assert_called_with(a=1, b=2, c=3)

这适用于 assert_called_with(), assert_called_once_with(), assert_has_calls()assert_any_call()。 当执行 Autospeccing 时,它还将应用于 mock 对象的方法调用。

3.4 版更變: 添加了在附带规格说明和自动规格说明的 mock 对象上的签名内省

class unittest.mock.PropertyMock(*args, **kwargs)

旨在作为类的特征属性或其他描述器使用的 mock。 PropertyMock 提供了 __get__()__set__() 方法以便你可以在它被提取时指定一个返回值。

当从一个对象提取 PropertyMock 实例时将不附带任何参数地调用该 mock。 如果设置它则调用该 mock 时将附带被设置的值。

>>> class Foo:
...     @property
...     def foo(self):
...         return 'something'
...     @foo.setter
...     def foo(self, value):
...         pass
...
>>> with patch('__main__.Foo.foo', new_callable=PropertyMock) as mock_foo:
...     mock_foo.return_value = 'mockity-mock'
...     this_foo = Foo()
...     print(this_foo.foo)
...     this_foo.foo = 6
...
mockity-mock
>>> mock_foo.mock_calls
[call(), call(6)]

由于 mock 属性的存储方式你无法直接将 PropertyMock 附加到一个 mock 对象。 但是你可以将它附加到 mock 类型对象:

>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> type(m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()
class unittest.mock.AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)

MagicMock 的异步版本。 AsyncMock 对象的行为方式将使该对象被识别为异步函数,其调用的结果将为可等待对象。

>>> mock = AsyncMock()
>>> asyncio.iscoroutinefunction(mock)
True
>>> inspect.isawaitable(mock())  
True

调用 mock() 的结果是一个异步函数,它在被等待之后将具有 side_effectreturn_value 的结果:

  • 如果 side_effect 是一个函数,则异步函数将返回该函数的结果,

  • 如果 side_effect 是一个异常,则异步函数将引发该异常,

  • 如果 side_effect 是一个可迭代对象,则异步函数将返回该可迭代对象的下一个值,但是,如果结果序列被耗尽,则会立即引发 StopAsyncIteration,

  • 如果 side_effect 未被定义,则异步函数将返回is not defined, the async function will return the value defined by return_value 所定义的值,因而,在默认情况下,异步函数会返回一个新的 AsyncMock 对象。

MockMagicMockspec 设为异步函数将导致在调用后返回一个协程对象。

>>> async def async_func(): pass
...
>>> mock = MagicMock(async_func)
>>> mock
<MagicMock spec='function' id='...'>
>>> mock()  
<coroutine object AsyncMockMixin._mock_call at ...>

Mock, MagicMockAsyncMockspec 设为带有异步和同步函数的类将自动删除其中的同步函数并将它们设为 MagicMock (如果上级 mock 是 AsyncMockMagicMock) 或者 Mock (如果上级 mock 是 Mock)。 所有异步函数都将为 AsyncMock

>>> class ExampleClass:
...     def sync_foo():
...         pass
...     async def async_foo():
...         pass
...
>>> a_mock = AsyncMock(ExampleClass)
>>> a_mock.sync_foo
<MagicMock name='mock.sync_foo' id='...'>
>>> a_mock.async_foo
<AsyncMock name='mock.async_foo' id='...'>
>>> mock = Mock(ExampleClass)
>>> mock.sync_foo
<Mock name='mock.sync_foo' id='...'>
>>> mock.async_foo
<AsyncMock name='mock.async_foo' id='...'>

3.8 版新加入.

assert_awaited()

断言 mock 已被等待至少一次。 请注意这是从被调用的对象中分离出来的,必须要使用 await 关键字:

>>> mock = AsyncMock()
>>> async def main(coroutine_mock):
...     await coroutine_mock
...
>>> coroutine_mock = mock()
>>> mock.called
True
>>> mock.assert_awaited()
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited.
>>> asyncio.run(main(coroutine_mock))
>>> mock.assert_awaited()
assert_awaited_once()

断言 mock 已被等待恰好一次。

>>> mock = AsyncMock()
>>> async def main():
...     await mock()
...
>>> asyncio.run(main())
>>> mock.assert_awaited_once()
>>> asyncio.run(main())
>>> mock.method.assert_awaited_once()
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
assert_awaited_with(*args, **kwargs)

断言上一次等待传入了指定的参数。

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_with('foo', bar='bar')
>>> mock.assert_awaited_with('other')
Traceback (most recent call last):
...
AssertionError: expected call not found.
Expected: mock('other')
Actual: mock('foo', bar='bar')
assert_awaited_once_with(*args, **kwargs)

断言 mock 已被等待恰好一次并且附带了指定的参数。

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_once_with('foo', bar='bar')
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_once_with('foo', bar='bar')
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
assert_any_await(*args, **kwargs)

断言 mock 已附带了指定的参数被等待。

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> asyncio.run(main('hello'))
>>> mock.assert_any_await('foo', bar='bar')
>>> mock.assert_any_await('other')
Traceback (most recent call last):
...
AssertionError: mock('other') await not found
assert_has_awaits(calls, any_order=False)

断言 mock 已附带了指定的调用被等待。 将针对这些等待检查 await_args_list 列表。

如果 any_order 为假值则等待必须是顺序进行的。 在指定的等待之前或之后还可以有额外的调用。

如果 any_order 为真值则等待可以是任意顺序的,但它们都必须在 await_args_list 中出现。

>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> calls = [call("foo"), call("bar")]
>>> mock.assert_has_awaits(calls)
Traceback (most recent call last):
...
AssertionError: Awaits not found.
Expected: [call('foo'), call('bar')]
Actual: []
>>> asyncio.run(main('foo'))
>>> asyncio.run(main('bar'))
>>> mock.assert_has_awaits(calls)
assert_not_awaited()

断言 mock 从未被等待过。

>>> mock = AsyncMock()
>>> mock.assert_not_awaited()
reset_mock(*args, **kwargs)

参见 Mock.reset_mock()。 还会将 await_count 设为 0,将 await_args 设为 None,并清空 await_args_list

await_count

一个追踪 mock 对象已被等待多少次的整数值。

>>> mock = AsyncMock()
>>> async def main():
...     await mock()
...
>>> asyncio.run(main())
>>> mock.await_count
1
>>> asyncio.run(main())
>>> mock.await_count
2
await_args

这可能为 None (如果 mock 从未被等待),或为该 mock 上一次被等待所附带的参数。 其功能与 Mock.call_args 相同。

>>> mock = AsyncMock()
>>> async def main(*args):
...     await mock(*args)
...
>>> mock.await_args
>>> asyncio.run(main('foo'))
>>> mock.await_args
call('foo')
>>> asyncio.run(main('bar'))
>>> mock.await_args
call('bar')
await_args_list

这是由对 mock 对象按顺序执行的所有等待组成的列表(因此该列表的长度即它被等待的次数)。 在有任何等待被执行之前它将为一个空列表。

>>> mock = AsyncMock()
>>> async def main(*args):
...     await mock(*args)
...
>>> mock.await_args_list
[]
>>> asyncio.run(main('foo'))
>>> mock.await_args_list
[call('foo')]
>>> asyncio.run(main('bar'))
>>> mock.await_args_list
[call('foo'), call('bar')]

调用

Mock 对象是可调用对象。 调用将把值集合作为 return_value 属性返回。 默认的返回值是一个新的 Mock 对象;它会在对返回值的首次访问(不论是显式访问还是通过调用 Mock)时被创建 —— 但它会被保存并且每次都返回相同的对象。

对该对象的调用将被记录在 call_argscall_args_list 等属性中。

如果设置了 side_effect 则它将在调用被记录之后被调用,因此如果 side_effect 引发了异常该调用仍然会被记录。

让一个 mock 在被调用时引发异常的最简单方式是将 side_effect 设为一个异常类或实例:

>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
  ...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
  ...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]

如果 side_effect 为函数则该函数所返回的对象就是调用该 mock 所返回的对象。 side_effect 函数在被调用时将附带与该 mock 相同的参数。 这允许你根据输入动态地改变返回值:

>>> def side_effect(value):
...     return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]

如果你想让该 mock 仍然返回默认的返回值(一个新的 mock对象),或是任何设定的返回值,那么有两种方式可以做到这一点。 从 side_effect 内部返回 mock.return_value,或者返回 DEFAULT:

>>> m = MagicMock()
>>> def side_effect(*args, **kwargs):
...     return m.return_value
...
>>> m.side_effect = side_effect
>>> m.return_value = 3
>>> m()
3
>>> def side_effect(*args, **kwargs):
...     return DEFAULT
...
>>> m.side_effect = side_effect
>>> m()
3

要移除一个 side_effect,并返回到默认的行为,请将 side_effect 设为 None:

>>> m = MagicMock(return_value=6)
>>> def side_effect(*args, **kwargs):
...     return 3
...
>>> m.side_effect = side_effect
>>> m()
3
>>> m.side_effect = None
>>> m()
6

side_effect 也可以是任意可迭代对象。 对该 mock 的重复调用将返回来自该可迭代对象的值(直到该可迭代对象被耗尽并导致 StopIteration 被引发):

>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
  ...
StopIteration

如果该可迭代对象有任何成员属于异常则它们将被引发而不是被返回:

>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
 ...
ValueError
>>> m()
66

删除属性

Mock 对象会根据需要创建属性。 这允许它们可以假装成任意类型的对象。

你可能想要一个 mock 对象在调用 hasattr() 时返回 False,或者在获取某个属性时引发 AttributeError。 你可以通过提供一个对象作为 mock 的 spec 属性来做到这点,但这并不总是很方便。

你可以通过删除属性来“屏蔽”它们。 属性一旦被删除,访问它将引发 AttributeError

>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
    ...
AttributeError: f

Mock 的名称与 name 属性

由于 "name" 是 Mock 构造器的参数之一,如果你想让你的 mock 对象具有 "name" 属性你不可以在创建时传入该参数。 有两个替代方式。 一个选项是使用 configure_mock():

>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'

一个更简单的选项是在 mock 创建之后简单地设置 "name" 属性:

>>> mock = MagicMock()
>>> mock.name = "foo"

附加 Mock 作为属性

当你附加一个 mock 作为另一个 mock 的属性(或作为返回值)时它会成为该 mock 的 "子对象"。 对子对象的调用会被记录在父对象的 method_callsmock_calls 属性中。 这适用于配置子 mock 然后将它们附加到父对象,或是将 mock 附加到将记录所有对子对象的调用的父对象上并允许你创建有关 mock 之间的调用顺序的断言:

>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]

这里有一个例外情况是如果 mock 设置了名称。 这允许你在出于某些理由不希望其发生时避免 "父对象" 的影响。

>>> mock = MagicMock()
>>> not_a_child = MagicMock(name='not-a-child')
>>> mock.attribute = not_a_child
>>> mock.attribute()
<MagicMock name='not-a-child()' id='...'>
>>> mock.mock_calls
[]

通过 patch() 创建的 mock 会被自动赋予名称。 要将具有名称的 mock 附加到父对象上你应当使用 attach_mock() 方法:

>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
...     with patch('__main__.thing2', return_value=None) as child2:
...         parent.attach_mock(child1, 'child1')
...         parent.attach_mock(child2, 'child2')
...         child1('one')
...         child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]
1

仅有的例外是魔术方法和属性(其名称前后都带有双下划线)。 Mock 不会创建它们而是将引发 AttributeError。 这是因为解释器将会经常隐式地请求这些方法,并且在它准备接受一个魔术方法却得到一个新的 Mock 对象时会 相当 困惑。 如果你需要魔术方法支持请参阅 魔术方法

patch 装饰器

patch 装饰器仅被用于在它们所装饰的函数作用域内部为对象添加补丁。 它们会自动为你执行去除补丁的处理,即使是在引发了异常的情况下。 所有这些函数都还可在 with 语句中使用或是作为类装饰器。

patch

備註

问题的关键是要在正确的命名空间中打补丁。 参见 where to patch 一节。

unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

patch() 可以作为函数装饰器、类装饰器或上下文管理器。 在函数或 with 语句的内部,target 会打上一个 new 对象补丁。 当函数/with 语句退出时补丁将被撤销。

如果 new 被省略,那么如果被打补丁的对象是一个异步函数则 target 将被替换为 AsyncMock 否则替换为 MagicMock。 如果 patch() 被用作装饰器并且 new 被省略,那么已创建的 mock 将作为一个附加参数传入被装饰的函数。 如果 patch() 被用作上下文管理器那么已创建的 mock 将被该上下文管理器所返回。is returned by the context manager.

target 应当为 'package.module.ClassName' 形式的字符串。 target 将被导入并且该指定对象会被替换为 new 对象,因此 target 必须是可以从你调用 patch() 的环境中导入的。 target 会在被装饰的函数被执行的时候被导入,而非在装饰的时候。

specspec_set 关键字参数会被传递给 MagicMock,如果 patch 为你创建了此对象的话。

此外你还可以传入 spec=Truespec_set=True,这将使 patch 将被模拟的对象作为 spec/spec_set 对象传入。

new_callable 允许你指定一个不同的类,或者可调用对象,它将被调用以创建 新的 对象。 在默认情况下将指定 AsyncMock 用于异步函数,MagicMock 用于其他函数。

另一种更强形式的 specautospec。 如果你设置了 autospec=True 则将以来自被替换对象的 spec 来创建 mock。 mock 的所有属性也将具有被替换对象相应属性的 spec。 被模拟的方法和函数将检查它们的参数并且如果使用了错误的签名调用它们则将引发 TypeError。 对于替换了一个类的 mock,它们的返回值(即‘实例’)将具有与该类相同的 spec。 请参阅 create_autospec() 函数以及 Autospeccing

除了 autospec=True 你还可以传入 autospec=some_object 以使用任意对象而不是被替换的对象作为 spec。

在默认情况下 patch() 将无法替换不存在的属性。 如果你传入 create=True,且该属性并不存在,则 patch 将在调用被打补丁的函数时为你创建该属性,并在退出被打补丁的函数时再次删除它。 这适用于编写针对生产代码在运行时创建的属性的测试。 它默认是被关闭的因为这具有危险性。 当它被开启时你将能够针对实际上并不存在的 API 编写通过测试!

備註

3.5 版更變: 如果你要给某个模块的内置函数打补丁则不必传入 create=True,它默认就会被添加。

Patch 可以被用作 TestCase 类装饰器。 其作用是装饰类中的每个测试方法。 当你的测试方法共享同一个补丁集时这将减少模板代码。 patch() 会通过查找以 patch.TEST_PREFIX 打头的名称来找到测试。 其默认值为 'test',这与 unittest 打到测试的方式一致。 你可以通过设置 patch.TEST_PREFIX 来指定其他的前缀。

Patch 可以通过 with 语句作为上下文管理器使用。 这时补丁将应用于 with 语句的缩进代码块。 如果你使用了 "as" 则打补丁的对象将被绑定到 "as" 之后的名称;这非常适用于当 patch() 为你创建 mock 对象的情况。

patch() 可接受任意关键字参数。 如果打补丁的对象是异步的则这些参数将被传给 AsyncMock,否则传给 MagicMock,或者是指定的 new_callable

patch.dict(...), patch.multiple(...)patch.object(...) 可用于其他使用场景。

patch() 作为函数装饰器,为你创建 mock 并将其传入被装饰的函数:

>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
...     print(mock_class is SomeClass)
...
>>> function(None)
True

为类打补丁将把该类替换为 MagicMock实例。 如果该类是在受测试的代码中被实例化的则它将为所要使用的 mock 的 return_value

如果该类被多次实例化则你可以使用 side_effect 来每次返回一个新 mock。 或者你也可以将 return_value 设为你希望的任何对象。

要在被打补丁的类的 实例 的方法上配置返回值你必须在 return_value 操作。 例如:

>>> class Class:
...     def method(self):
...         pass
...
>>> with patch('__main__.Class') as MockClass:
...     instance = MockClass.return_value
...     instance.method.return_value = 'foo'
...     assert Class() is instance
...     assert Class().method() == 'foo'
...

如果你使用 specspec_set 并且 patch() 替换的是 class,那么所创建的 mock 的返回值将具有同样的 spec。

>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()

new_callable 参数适用于当你想要使用其他类来替代所创建的 mock 默认的 MagicMock 的场合。 例如,如果你想要使用 NonCallableMock:

>>> thing = object()
>>> with patch('__main__.thing', new_callable=NonCallableMock) as mock_thing:
...     assert thing is mock_thing
...     thing()
...
Traceback (most recent call last):
  ...
TypeError: 'NonCallableMock' object is not callable

另一个使用场景是用 io.StringIO 实例来替换某个对象:

>>> from io import StringIO
>>> def foo():
...     print('Something')
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
...     foo()
...     assert mock_stdout.getvalue() == 'Something\n'
...
>>> test()

patch() 为你创建 mock 时,通常你需要做的第一件事就是配置该 mock。 某些配置可以在对 patch 的调用中完成。 你在调用时传入的任何关键字参数都将被用来在所创建的 mock 上设置属性:

>>> patcher = patch('__main__.thing', first='one', second='two')
>>> mock_thing = patcher.start()
>>> mock_thing.first
'one'
>>> mock_thing.second
'two'

除了所创建的 mock 的属性上的属性,例如 return_valueside_effect,还可以配置子 mock 的属性。 将这些属性直接作为关键字参数传入在语义上是无效的,但是仍然能够使用 ** 将以这些属性为键的字典扩展至一个 patch() 调用中

>>> config = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> patcher = patch('__main__.thing', **config)
>>> mock_thing = patcher.start()
>>> mock_thing.method()
3
>>> mock_thing.other()
Traceback (most recent call last):
  ...
KeyError

在默认情况下,尝试给某个模块中并不存在的函数(或者某个类中的方法或属性)打补丁将会失败并引发 AttributeError:

>>> @patch('sys.non_existing_attribute', 42)
... def test():
...     assert sys.non_existing_attribute == 42
...
>>> test()
Traceback (most recent call last):
  ...
AttributeError: <module 'sys' (built-in)> does not have the attribute 'non_existing_attribute'

但在对 patch() 的调用中添加 create=True 将使之前示例的效果符合预期:

>>> @patch('sys.non_existing_attribute', 42, create=True)
... def test(mock_stdout):
...     assert sys.non_existing_attribute == 42
...
>>> test()

3.8 版更變: 如果目标为异步函数那么 patch() 现在将返回一个 AsyncMock

patch.object

patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

用一个 mock 对象为对象 (target) 中指定名称的成员 (attribute) 打补丁。

patch.object() 可以被用作装饰器、类装饰器或上下文管理器。 new, spec, create, spec_set, autospecnew_callable 等参数的含义与 patch() 的相同。 与 patch() 类似,patch.object() 接受任意关键字参数用于配置它所创建的 mock 对象。

当用作类装饰器时 patch.object() 将认可 patch.TEST_PREFIX 作为选择所要包装方法的标准。

你可以附带三个参数或两个参数来调用 patch.object()。 三个参数的形式将接受要打补丁的对象、属性的名称以及将要替换该属性的对象。

将附带两个参数的形式调用时你将省略替换对象,还会为你创建一个 mock 并作为附加参数传入被装饰的函数:

>>> @patch.object(SomeClass, 'class_method')
... def test(mock_method):
...     SomeClass.class_method(3)
...     mock_method.assert_called_with(3)
...
>>> test()

传给 patch.object()spec, create 和其他参数的含义与 patch() 的同名参数相同。

patch.dict

patch.dict(in_dict, values=(), clear=False, **kwargs)

为一个字典或字典类对象打补丁,并在测试之后将该目录恢复到其初始状态。

in_dict 可以是一个字典或映射类容器。 如果它是一个映射则它必须至少支持获取、设置和删除条目以及对键执行迭代。

in_dict 也可以是一个指定字典名称的字符串,然后将通过导入操作来获取该字典。

values 可以是一个要在字典中设置的值的字典。 values 也可以是一个包含 (key, value) 对的可迭代对象。

如果 clear 为真值则该字典将在设置新值之前先被清空。

patch.dict() 也可以附带任意关键字参数调用以设置字典中的值。

3.8 版更變: 现在当 patch.dict() 被用作上下文管理器时将返回被打补丁的字典。now returns the patched dictionary when used as a context manager.

patch.dict() 可被用作上下文管理器、装饰器或类装饰器:

>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
...     assert foo == {'newkey': 'newvalue'}
>>> test()
>>> assert foo == {}

当被用作类装饰器时 patch.dict() 将认可 patch.TEST_PREFIX (默认值为 'test') 作为选择所在包装方法的标准:

>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
...     def test_sample(self):
...         self.assertEqual(os.environ['newkey'], 'newvalue')

如果你在为你的测试使用不同的前缀,你可以通过设置 patch.TEST_PREFIX 来将不同的前缀告知打补丁方。 有关如何修改该值的详情请参阅 TEST_PREFIX

patch.dict() 可被用来向一个字典添加成员,或者简单地让测试修改一个字典,并确保当测试结束时恢复该字典。

>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
...     assert foo == {'newkey': 'newvalue'}
...     assert patched_foo == {'newkey': 'newvalue'}
...     # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
...     patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
...     print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ

可以在 patch.dict() 调用中使用关键字来设置字典的值:

>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
...     import mymodule
...     mymodule.function('some', 'args')
...
'fish'

patch.dict() 可以用于实际上不是字典的字典类对象。 它们最少必须支持条目获取、设置、删除以及迭代或成员检测两者中的一个。 这对应于魔术方法 __getitem__(), __setitem__(), __delitem__() 以及 __iter__()__contains__()

>>> class Container:
...     def __init__(self):
...         self.values = {}
...     def __getitem__(self, name):
...         return self.values[name]
...     def __setitem__(self, name, value):
...         self.values[name] = value
...     def __delitem__(self, name):
...         del self.values[name]
...     def __iter__(self):
...         return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
...     assert thing['one'] == 2
...     assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']

patch.multiple

patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

在单个调用中执行多重补丁。 它接受要打补丁的对象(一个对象或一个通过导入来获取对象的字符串)以及用于补丁的关键字参数:

with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'):
    ...

如果你希望 patch.multiple() 为你创建 mock 则要使用 DEFAULT 作为值。 在此情况下所创建的 mock 会通过关键字参数传入被装饰的函数,而当 patch.multiple() 被用作上下文管理器时则将返回一个字典。

patch.multiple() 可以被用作装饰器、类装饰器或上下文管理器。 spec, spec_set, create, autospecnew_callable 等参数的含义与 patch() 的相同。 这些参数将被应用到 patch.multiple() 所打的 所有 补丁。

当被用作类装饰器时 patch.multiple() 将认可 patch.TEST_PREFIX 作为选择所要包装方法的标准。

如果你希望 patch.multiple() 为你创建 mock,那么你可以使用 DEFAULT 作为值。 如果你使用 patch.multiple() 作为装饰器则所创建的 mock 会作为关键字参数传入被装饰的函数。

>>> thing = object()
>>> other = object()

>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
...     assert isinstance(thing, MagicMock)
...     assert isinstance(other, MagicMock)
...
>>> test_function()

patch.multiple() 可以与其他 patch 装饰器嵌套使用,但要将作为关键字传入的参数要放在 patch() 所创建的标准参数 之后:

>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
...     assert 'other' in repr(other)
...     assert 'thing' in repr(thing)
...     assert 'exit' in repr(mock_exit)
...
>>> test_function()

如果 patch.multiple() 被用作上下文管理器,则上下文管理器的返回值将是一个以所创建的 mock 的名称为键的字典:

>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
...     assert 'other' in repr(values['other'])
...     assert 'thing' in repr(values['thing'])
...     assert values['thing'] is thing
...     assert values['other'] is other
...

补丁方法: start 和 stop

所有补丁对象都具有 start()stop() 方法。 使用这些方法可以更简单地在 setUp 方法上打补丁,还可以让你不必嵌套使用装饰器或 with 语句就能打多重补丁。

要使用这些方法请按正常方式调用 patch(), patch.object()patch.dict() 并保留一个指向所返回 patcher 对象的引用。 然后你可以调用 start() 来原地打补丁并调用 stop() 来恢复它。

如果你使用 patch() 来创建自己的 mock 那么将可通过调用 patcher.start 来返回它。

>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock

此操作的一个典型应用场景是在一个 TestCasesetUp 方法中执行多重补丁:

>>> class MyTest(unittest.TestCase):
...     def setUp(self):
...         self.patcher1 = patch('package.module.Class1')
...         self.patcher2 = patch('package.module.Class2')
...         self.MockClass1 = self.patcher1.start()
...         self.MockClass2 = self.patcher2.start()
...
...     def tearDown(self):
...         self.patcher1.stop()
...         self.patcher2.stop()
...
...     def test_something(self):
...         assert package.module.Class1 is self.MockClass1
...         assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()

警示

如果你要使用这个技巧则你必须通过调用 stop 来确保补丁被“恢复”。 这可能要比你想像的更麻烦,因为如果在 setUp 中引发了异常那么 tearDown 将不会被调用。 unittest.TestCase.addCleanup() 可以简化此操作:

>>> class MyTest(unittest.TestCase):
...     def setUp(self):
...         patcher = patch('package.module.Class')
...         self.MockClass = patcher.start()
...         self.addCleanup(patcher.stop)
...
...     def test_something(self):
...         assert package.module.Class is self.MockClass
...

一项额外的好处是你不再需要保留指向 patcher 对象的引用。

还可以通过使用 patch.stopall() 来停止已启动的所有补丁。

patch.stopall()

停止所有激活的补丁。 仅会停止通过 start 启动的补丁。

为内置函数打补丁

你可以为一个模块中的任何内置函数打补丁。 以以示例是为内置函数 ord() 打补丁:

>>> @patch('__main__.ord')
... def test(mock_ord):
...     mock_ord.return_value = 101
...     print(ord('c'))
...
>>> test()
101

TEST_PREFIX

所有补丁都可被用作类装饰器。 当以这种方式使用时它们将会包装类中的每个测试方法。 补丁会将以名字以 'test' 开头的方法识别为测试方法。 这与 unittest.TestLoader 查找测试方法的默认方式相同。

你可能会想要为你的测试使用不同的前缀。 你可以通过设置 patch.TEST_PREFIX 来告知打补丁方不同的前缀:

>>> patch.TEST_PREFIX = 'foo'
>>> value = 3
>>>
>>> @patch('__main__.value', 'not three')
... class Thing:
...     def foo_one(self):
...         print(value)
...     def foo_two(self):
...         print(value)
...
>>>
>>> Thing().foo_one()
not three
>>> Thing().foo_two()
not three
>>> value
3

嵌套补丁装饰器

如果你想要应用多重补丁那么你可以简单地堆叠多个装饰器。

你可以使用以下模式来堆叠多个补丁装饰器:

>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
...     assert SomeClass.static_method is mock1
...     assert SomeClass.class_method is mock2
...     SomeClass.static_method('foo')
...     SomeClass.class_method('bar')
...     return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')

请注意装饰器是从下往上被应用的。 这是 Python 应用装饰器的标准方式。 被创建并传入你的测试函数的 mock 的顺序也将匹配这个顺序。

补丁的目标

patch() 通过(临时性地)修改某一个对象的 名称 指向另一个对象来发挥作用。 可以有多个名称指向任意单独对象,因此要让补丁起作用你必须确保已为被测试的系统所使用的名称打上补丁。

基本原则是你要在对象 被查找 的地方打补丁,这不一定就是它被定义的地方。 一组示例将有助于厘清这一点。

想像我们有一个想要测试的具有如下结构的项目:

a.py
    -> Defines SomeClass

b.py
    -> from a import SomeClass
    -> some_function instantiates SomeClass

现在我们要测试 some_function 但我们想使用 patch() 来模拟 SomeClass。 问题在于当我们导入模块 b 时,我们将必须让它从模块 a 导入 SomeClass。 如果我们使用 patch() 来模拟 a.SomeClass 那么它将不会对我们的测试造成影响;模块 b 已经拥有对 真正的 SomeClass 的引用因此看上去我们的补丁不会有任何影响。

关键在于对 SomeClass 打补丁操作是在它被使用(或它被查找)的地方。 在此情况下实际上 some_function 将在模块 b 中查找 SomeClass,而我们已经在那里导入了它。 补丁看上去应该是这样:

@patch('b.SomeClass')

但是,再考虑另一个场景,其中不是 from a import SomeClass 而是模块 b 执行了 import a 并且 some_function 使用了 a.SomeClass。 这两个导入形式都很常见。 在这种情况下我们要打补丁的类将在该模块中被查找因而我们必须改为对 a.SomeClass 打补丁:

@patch('a.SomeClass')

对描述器和代理对象打补丁

patchpatch.object 都能正确地对描述器打补丁并恢复:包括类方法、静态方法和特征属性。 你应当在 而不是在实例上为它们打补丁。 它们还适用于代理属性访问的 某些 对象,例如 django 设置对象

MagicMock 与魔术方法支持

模拟魔术方法

Mock 支持模拟 Python 协议方法,或称“魔术方法”。 这允许 mock 对象替代容器或其他实现了 Python 协议的对象。

因为查找魔术方法的方式不同于普通方法 2,这种支持采用了特别的实现。 这意味着只有特定的魔术方法受到支持。 受支持的是 几乎 所有魔术方法。 如果有你需要但被遗漏的请告知我们。

你可以通过在某个函数或 mock 实例中设置魔术方法来模拟它们。 如果你是使用函数则它 必须 接受 self 作为第一个参数 3

>>> def __str__(self):
...     return 'fooble'
...
>>> mock = Mock()
>>> mock.__str__ = __str__
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__str__ = Mock()
>>> mock.__str__.return_value = 'fooble'
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__iter__ = Mock(return_value=iter([]))
>>> list(mock)
[]

一个这样的应用场景是在 with 语句中模拟作为上下文管理器的对象:

>>> mock = Mock()
>>> mock.__enter__ = Mock(return_value='foo')
>>> mock.__exit__ = Mock(return_value=False)
>>> with mock as m:
...     assert m == 'foo'
...
>>> mock.__enter__.assert_called_with()
>>> mock.__exit__.assert_called_with(None, None, None)

对魔术方法的调用不会在 method_calls 中出现,但它们会被记录在 mock_calls 中。

備註

如果你使用 spec 关键字参数来创建 mock 那么尝试设置不包含在 spec 中的魔术方法将引发 AttributeError

受支持魔术方法的完整列表如下:

  • __hash____sizeof____repr____str__

  • __dir____format____subclasses__

  • __round____floor____trunc____ceil__

  • 比较运算: __lt__, __gt__, __le__, __ge__, __eq____ne__

  • 容器方法: __getitem__, __setitem__, __delitem__, __contains__, __len__, __iter__, __reversed____missing__

  • 上下文管理器: __enter__, __exit__, __aenter____aexit__

  • 单目数值运算方法: __neg__, __pos____invert__

  • 数值运算方法 (包括右手和原地形式): __add__, __sub__, __mul__, __matmul__, __div__, __truediv__, __floordiv__, __mod__, __divmod__, __lshift__, __rshift__, __and__, __xor__, __or____pow__

  • 数值转换方法: __complex__, __int__, __float____index__

  • 描述器方法: __get__, __set__ and __delete__

  • 封存方法: __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate____setstate__

  • 文件系统路径表示: __fspath__

  • 异步迭代方法: __aiter__ and __anext__

3.8 版更變: 增加了对 os.PathLike.__fspath__() 的支持。

3.8 版更變: 增加了对 __aenter__, __aexit__, __aiter____anext__ 的支持。

下列方法均存在但是 不受 支持,因为它们或者被 mock 所使用,或者无法动态设置,或者可能导致问题:

  • __getattr____setattr____init____new__

  • __prepare____instancecheck____subclasscheck____del__

MagicMock

存在两个版本的 MagicMock: MagicMockNonCallableMagicMock.

class unittest.mock.MagicMock(*args, **kw)

MagicMock 是包含了大部分魔术方法的默认实现的 Mock 的子类。 你可以使用 MagicMock 而无须自行配置魔术方法。without having to configure the magic methods yourself.

构造器形参的含义与 Mock 的相同。

如果你使用了 specspec_set 参数则将 只有 存在于 spec 中的魔术方法会被创建。

class unittest.mock.NonCallableMagicMock(*args, **kw)

MagicMock 的不可调用版本。

其构造器的形参具有与 MagicMock 相同的含义,区别在于 return_valueside_effect 在不可调用的 mock 上没有意义。

魔术方法是通过 MagicMock 对象来设置的,因此你可以用通常的方式来配置它们并使用它们:

>>> mock = MagicMock()
>>> mock[3] = 'fish'
>>> mock.__setitem__.assert_called_with(3, 'fish')
>>> mock.__getitem__.return_value = 'result'
>>> mock[2]
'result'

在默认情况下许多协议方法都需要返回特定类型的对象。 这些方法都预先配置了默认的返回值,以便它们在你对返回值不感兴趣时可以不做任何事就能被使用。 如果你想要修改默认值则你仍然可以手动 设置 返回值。

方法及其默认返回值:

  • __lt__NotImplemented

  • __gt__NotImplemented

  • __le__NotImplemented

  • __ge__NotImplemented

  • __int__1

  • __contains__False

  • __len__0

  • __iter__iter([])

  • __exit__False

  • __aexit__False

  • __complex__1j

  • __float__1.0

  • __bool__True

  • __index__1

  • __hash__: mock 的默认 hash

  • __str__: mock 的默认 str

  • __sizeof__: mock 的默认 sizeof

舉例來說:

>>> mock = MagicMock()
>>> int(mock)
1
>>> len(mock)
0
>>> list(mock)
[]
>>> object() in mock
False

两个相等性方法 __eq__()__ne__() 是特殊的。 它们基于标识号进行默认的相等性比较,使用 side_effect 属性,除非你修改它们的返回值以返回其他内容:

>>> MagicMock() == 3
False
>>> MagicMock() != 3
True
>>> mock = MagicMock()
>>> mock.__eq__.return_value = True
>>> mock == 3
True

MagicMock.__iter__() 的返回值可以是任意可迭代对象而不要求必须是迭代器:

>>> mock = MagicMock()
>>> mock.__iter__.return_value = ['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']

如果返回值 迭代器,则对其执行一次迭代就会将它耗尽因而后续执行的迭代将会输出空列表:

>>> mock.__iter__.return_value = iter(['a', 'b', 'c'])
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
[]

MagicMock 已配置了所有受支持的魔术方法,只有某些晦涩和过时的魔术方法是例外。 如果你需要仍然可以设置它们。

MagicMock 中受到支持但默认未被设置的魔术方法有:

  • __subclasses__

  • __dir__

  • __format__

  • __get____set____delete__

  • __reversed____missing__

  • __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate____setstate__

  • __getformat____setformat__

2

魔术方法 应当 是在类中而不是在实例中查找。 不同的 Python 版本对这个规则的应用并不一致。 受支持的协议方法应当适用于所有受支持的 Python 版本。

3

该函数基本上是与类挂钩的,但每个 Mock 实例都会与其他实例保持隔离。

辅助对象

sentinel

unittest.mock.sentinel

sentinel 对象提供了一种为你的测试提供独特对象的便捷方式。

属性是在你通过名称访问它们时按需创建的。 访问相同的属性将始终返回相同的对象。 返回的对象会有一个合理的 repr 以使测试失败消息易于理解。

3.7 版更變: 现在 sentinel 属性会在它们被 copypickle 时保存其标识。

Sometimes when testing you need to test that a specific object is passed as an argument to another method, or returned. It can be common to create named sentinel objects to test this. sentinel provides a convenient way of creating and testing the identity of objects like this.

In this example we monkey patch method to return sentinel.some_object:

>>> real = ProductionClass()
>>> real.method = Mock(name="method")
>>> real.method.return_value = sentinel.some_object
>>> result = real.method()
>>> assert result is sentinel.some_object
>>> result
sentinel.some_object

DEFAULT

unittest.mock.DEFAULT

The DEFAULT object is a pre-created sentinel (actually sentinel.DEFAULT). It can be used by side_effect functions to indicate that the normal return value should be used.

call

unittest.mock.call(*args, **kwargs)

call() is a helper object for making simpler assertions, for comparing with call_args, call_args_list, mock_calls and method_calls. call() can also be used with assert_has_calls().

>>> m = MagicMock(return_value=None)
>>> m(1, 2, a='foo', b='bar')
>>> m()
>>> m.call_args_list == [call(1, 2, a='foo', b='bar'), call()]
True
call.call_list()

For a call object that represents multiple calls, call_list() returns a list of all the intermediate calls as well as the final call.

call_list is particularly useful for making assertions on "chained calls". A chained call is multiple calls on a single line of code. This results in multiple entries in mock_calls on a mock. Manually constructing the sequence of calls can be tedious.

call_list() can construct the sequence of calls from the same chained call:

>>> m = MagicMock()
>>> m(1).method(arg='foo').other('bar')(2.0)
<MagicMock name='mock().method().other()()' id='...'>
>>> kall = call(1).method(arg='foo').other('bar')(2.0)
>>> kall.call_list()
[call(1),
 call().method(arg='foo'),
 call().method().other('bar'),
 call().method().other()(2.0)]
>>> m.mock_calls == kall.call_list()
True

A call object is either a tuple of (positional args, keyword args) or (name, positional args, keyword args) depending on how it was constructed. When you construct them yourself this isn't particularly interesting, but the call objects that are in the Mock.call_args, Mock.call_args_list and Mock.mock_calls attributes can be introspected to get at the individual arguments they contain.

The call objects in Mock.call_args and Mock.call_args_list are two-tuples of (positional args, keyword args) whereas the call objects in Mock.mock_calls, along with ones you construct yourself, are three-tuples of (name, positional args, keyword args).

You can use their "tupleness" to pull out the individual arguments for more complex introspection and assertions. The positional arguments are a tuple (an empty tuple if there are no positional arguments) and the keyword arguments are a dictionary:

>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> kall.args
(1, 2, 3)
>>> kall.kwargs
{'arg': 'one', 'arg2': 'two'}
>>> kall.args is kall[0]
True
>>> kall.kwargs is kall[1]
True
>>> m = MagicMock()
>>> m.foo(4, 5, 6, arg='two', arg2='three')
<MagicMock name='mock.foo()' id='...'>
>>> kall = m.mock_calls[0]
>>> name, args, kwargs = kall
>>> name
'foo'
>>> args
(4, 5, 6)
>>> kwargs
{'arg': 'two', 'arg2': 'three'}
>>> name is m.mock_calls[0][0]
True

create_autospec

unittest.mock.create_autospec(spec, spec_set=False, instance=False, **kwargs)

Create a mock object using another object as a spec. Attributes on the mock will use the corresponding attribute on the spec object as their spec.

Functions or methods being mocked will have their arguments checked to ensure that they are called with the correct signature.

If spec_set is True then attempting to set attributes that don't exist on the spec object will raise an AttributeError.

If a class is used as a spec then the return value of the mock (the instance of the class) will have the same spec. You can use a class as the spec for an instance object by passing instance=True. The returned mock will only be callable if instances of the mock are callable.

create_autospec() also takes arbitrary keyword arguments that are passed to the constructor of the created mock.

See Autospeccing for examples of how to use auto-speccing with create_autospec() and the autospec argument to patch().

3.8 版更變: create_autospec() now returns an AsyncMock if the target is an async function.

ANY

unittest.mock.ANY

Sometimes you may need to make assertions about some of the arguments in a call to mock, but either not care about some of the arguments or want to pull them individually out of call_args and make more complex assertions on them.

To ignore certain arguments you can pass in objects that compare equal to everything. Calls to assert_called_with() and assert_called_once_with() will then succeed no matter what was passed in.

>>> mock = Mock(return_value=None)
>>> mock('foo', bar=object())
>>> mock.assert_called_once_with('foo', bar=ANY)

ANY can also be used in comparisons with call lists like mock_calls:

>>> m = MagicMock(return_value=None)
>>> m(1)
>>> m(1, 2)
>>> m(object())
>>> m.mock_calls == [call(1), call(1, 2), ANY]
True

FILTER_DIR

unittest.mock.FILTER_DIR

FILTER_DIR is a module level variable that controls the way mock objects respond to dir() (only for Python 2.6 or more recent). The default is True, which uses the filtering described below, to only show useful members. If you dislike this filtering, or need to switch it off for diagnostic purposes, then set mock.FILTER_DIR = False.

With filtering on, dir(some_mock) shows only useful attributes and will include any dynamically created attributes that wouldn't normally be shown. If the mock was created with a spec (or autospec of course) then all the attributes from the original are shown, even if they haven't been accessed yet:

>>> dir(Mock())
['assert_any_call',
 'assert_called',
 'assert_called_once',
 'assert_called_once_with',
 'assert_called_with',
 'assert_has_calls',
 'assert_not_called',
 'attach_mock',
 ...
>>> from urllib import request
>>> dir(Mock(spec=request))
['AbstractBasicAuthHandler',
 'AbstractDigestAuthHandler',
 'AbstractHTTPHandler',
 'BaseHandler',
 ...

Many of the not-very-useful (private to Mock rather than the thing being mocked) underscore and double underscore prefixed attributes have been filtered from the result of calling dir() on a Mock. If you dislike this behaviour you can switch it off by setting the module level switch FILTER_DIR:

>>> from unittest import mock
>>> mock.FILTER_DIR = False
>>> dir(mock.Mock())
['_NonCallableMock__get_return_value',
 '_NonCallableMock__get_side_effect',
 '_NonCallableMock__return_value_doc',
 '_NonCallableMock__set_return_value',
 '_NonCallableMock__set_side_effect',
 '__call__',
 '__class__',
 ...

Alternatively you can just use vars(my_mock) (instance members) and dir(type(my_mock)) (type members) to bypass the filtering irrespective of mock.FILTER_DIR.

mock_open

unittest.mock.mock_open(mock=None, read_data=None)

A helper function to create a mock to replace the use of open(). It works for open() called directly or used as a context manager.

The mock argument is the mock object to configure. If None (the default) then a MagicMock will be created for you, with the API limited to methods or attributes available on standard file handles.

read_data is a string for the read(), readline(), and readlines() methods of the file handle to return. Calls to those methods will take data from read_data until it is depleted. The mock of these methods is pretty simplistic: every time the mock is called, the read_data is rewound to the start. If you need more control over the data that you are feeding to the tested code you will need to customize this mock for yourself. When that is insufficient, one of the in-memory filesystem packages on PyPI can offer a realistic filesystem for testing.

3.4 版更變: Added readline() and readlines() support. The mock of read() changed to consume read_data rather than returning it on each call.

3.5 版更變: read_data is now reset on each call to the mock.

3.8 版更變: Added __iter__() to implementation so that iteration (such as in for loops) correctly consumes read_data.

Using open() as a context manager is a great way to ensure your file handles are closed properly and is becoming common:

with open('/some/path', 'w') as f:
    f.write('something')

The issue is that even if you mock out the call to open() it is the returned object that is used as a context manager (and has __enter__() and __exit__() called).

Mocking context managers with a MagicMock is common enough and fiddly enough that a helper function is useful.

>>> m = mock_open()
>>> with patch('__main__.open', m):
...     with open('foo', 'w') as h:
...         h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
 call().__enter__(),
 call().write('some stuff'),
 call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')

And for reading files:

>>> with patch('__main__.open', mock_open(read_data='bibble')) as m:
...     with open('foo') as h:
...         result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'bibble'

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. In addition mocked functions / methods have the same call signature as the original so they raise a TypeError if they are called incorrectly.

Before I explain how auto-speccing works, here's why it is needed.

Mock is a very powerful and flexible object, but it suffers from two flaws when used to mock out objects from a system under test. One of these flaws is specific to the Mock api and the other is a more general problem with using mock objects.

First the problem specific to Mock. Mock has two assert methods that are extremely handy: assert_called_with() and assert_called_once_with().

>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
>>> mock(1, 2, 3)
>>> mock.assert_called_once_with(1, 2, 3)
Traceback (most recent call last):
 ...
AssertionError: Expected 'mock' to be called once. Called 2 times.

Because mocks auto-create attributes on demand, and allow you to call them with arbitrary arguments, if you misspell one of these assert methods then your assertion is gone:

>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assret_called_once_with(4, 5, 6)  # Intentional typo!

Your tests can pass silently and incorrectly because of the typo.

The second issue is more general to mocking. If you refactor some of your code, rename members and so on, any tests for code that is still using the old api but uses mocks instead of the real objects will still pass. This means your tests can all pass even though your code is broken.

Note that this is another reason why you need integration tests as well as unit tests. Testing everything in isolation is all fine and dandy, but if you don't test how your units are "wired together" there is still lots of room for bugs that tests might have caught.

mock already provides a feature to help with this, called speccing. If you use a class or instance as the spec for a mock then you can only access attributes on the mock that exist on the real class:

>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with  # Intentional typo!
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'assret_called_with'

The spec only applies to the mock itself, so we still have the same issue with any methods on the mock:

>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with()  # Intentional typo!

Auto-speccing solves this problem. You can either pass autospec=True to patch() / patch.object() or use the create_autospec() function to create a mock with a spec. If you use the autospec=True argument to patch() then the object that is being replaced will be used as the spec object. Because the speccing is done "lazily" (the spec is created as attributes on the mock are accessed) you can use it with very complex or deeply nested objects (like modules that import modules that import modules) without a big performance hit.

Here's an example of it in use:

>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>

You can see that request.Request has a spec. request.Request takes two arguments in the constructor (one of which is self). Here's what happens if we try to call it incorrectly:

>>> req = request.Request()
Traceback (most recent call last):
 ...
TypeError: <lambda>() takes at least 2 arguments (1 given)

The spec also applies to instantiated classes (i.e. the return value of specced mocks):

>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>

Request objects are not callable, so the return value of instantiating our mocked out request.Request is a non-callable mock. With the spec in place any typos in our asserts will raise the correct error:

>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with  # Intentional typo!
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')

In many cases you will just be able to add autospec=True to your existing patch() calls and then be protected against bugs due to typos and api changes.

As well as using autospec through patch() there is a create_autospec() for creating autospecced mocks directly:

>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>

This isn't without caveats and limitations however, which is why it is not the default behaviour. In order to know what attributes are available on the spec object, autospec has to introspect (access attributes) the spec. As you traverse attributes on the mock a corresponding traversal of the original object is happening under the hood. If any of your specced objects have properties or descriptors that can trigger code execution then you may not be able to use autospec. On the other hand it is much better to design your objects so that introspection is safe 4.

A more serious problem is that it is common for instance attributes to be created in the __init__() method and not to exist on the class at all. autospec can't know about any dynamically created attributes and restricts the api to visible attributes.

>>> class Something:
...   def __init__(self):
...     self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
...   thing = Something()
...   thing.a
...
Traceback (most recent call last):
  ...
AttributeError: Mock object has no attribute 'a'

There are a few different ways of resolving this problem. The easiest, but not necessarily the least annoying, way is to simply set the required attributes on the mock after creation. Just because autospec doesn't allow you to fetch attributes that don't exist on the spec it doesn't prevent you setting them:

>>> with patch('__main__.Something', autospec=True):
...   thing = Something()
...   thing.a = 33
...

There is a more aggressive version of both spec and autospec that does prevent you setting non-existent attributes. This is useful if you want to ensure your code only sets valid attributes too, but obviously it prevents this particular scenario:

>>> with patch('__main__.Something', autospec=True, spec_set=True):
...   thing = Something()
...   thing.a = 33
...
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'a'

Probably the best way of solving the problem is to add class attributes as default values for instance members initialised in __init__(). Note that if you are only setting default attributes in __init__() then providing them via class attributes (shared between instances of course) is faster too. e.g.

class Something:
    a = 33

This brings up another issue. It is relatively common to provide a default value of None for members that will later be an object of a different type. None would be useless as a spec because it wouldn't let you access any attributes or methods on it. As None is never going to be useful as a spec, and probably indicates a member that will normally of some other type, autospec doesn't use a spec for members that are set to None. These will just be ordinary mocks (well - MagicMocks):

>>> class Something:
...     member = None
...
>>> mock = create_autospec(Something)
>>> mock.member.foo.bar.baz()
<MagicMock name='mock.member.foo.bar.baz()' id='...'>

If modifying your production classes to add defaults isn't to your liking then there are more options. One of these is simply to use an instance as the spec rather than the class. The other is to create a subclass of the production class and add the defaults to the subclass without affecting the production class. Both of these require you to use an alternative object as the spec. Thankfully patch() supports this - you can simply pass the alternative object as the autospec argument:

>>> class Something:
...   def __init__(self):
...     self.a = 33
...
>>> class SomethingForTest(Something):
...   a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>
4

This only applies to classes or already instantiated objects. Calling a mocked class to create a mock instance does not create a real instance. It is only attribute lookups - along with calls to dir() - that are done.

Sealing mocks

unittest.mock.seal(mock)

Seal will disable the automatic creation of mocks when accessing an attribute of the mock being sealed or any of its attributes that are already mocks recursively.

If a mock instance with a name or a spec is assigned to an attribute it won't be considered in the sealing chain. This allows one to prevent seal from fixing part of the mock object.

>>> mock = Mock()
>>> mock.submock.attribute1 = 2
>>> mock.not_submock = mock.Mock(name="sample_name")
>>> seal(mock)
>>> mock.new_attribute  # This will raise AttributeError.
>>> mock.submock.attribute2  # This will raise AttributeError.
>>> mock.not_submock.attribute2  # This won't raise.

3.7 版新加入.