unittest.mock ---模拟对象库

3.3 新版功能.

源代码: Lib/unittest/mock.py


unittest.mock 是一个在Python中测试的库。它允许您用模拟对象替换测试中的系统部分,并对它们的使用方式作出断言。

unittest.mock 提供核心 Mock 类删除了在整个测试套件中创建大量存根的需要。在执行操作之后,您可以断言使用了哪些方法/属性以及调用它们的参数。您还可以指定返回值并以正常方式设置所需的属性。

此外,mock还提供了 patch() 在测试范围内处理修补模块和类级属性的装饰器,以及 sentinel 用于创建唯一对象。查看 quick guide 例如如何使用 MockMagicMockpatch() .

mock非常容易使用,设计用于 unittest . 模拟是基于“操作->断言”模式,而不是许多模拟框架使用的“记录->重播”。

有一个反向端口 unittest.mock 对于早期版本的python,可用作 mock on PyPI .

快速指南

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 允许您执行副作用,包括在调用模拟时引发异常:

>>> 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有许多其他的方法,您可以配置它并控制它的行为。例如 spec 参数将mock配置为从另一个对象获取其规范。尝试访问规范中不存在的模拟上的属性或方法将失败,原因是 AttributeError .

这个 patch() decorator/context manager使模拟测试模块中的类或对象变得容易。您指定的对象将在测试期间替换为模拟对象(或其他对象),并在测试结束时还原:

>>> 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()

注解

当嵌套修补程序装饰器时,模拟将按它们应用的相同顺序传入装饰函数(正常 Python 命令应用装饰器)。这意味着从下到上,因此在上面的示例中, module.ClassName1 先通过。

patch() 在查找对象的名称空间中修补对象很重要。这通常很简单,但为了快速阅读 where to 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的模拟 magic methods . 使用魔法方法的最简单方法是 MagicMock 类。它允许您执行以下操作:

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

mock允许您将函数(或其他mock实例)分配给magic方法,它们将被适当地调用。这个 MagicMock 类只是一个模拟变量,它有为您预先创建的所有魔术方法(好吧,不管怎样,所有有用的方法)。

下面是一个将魔术方法用于普通模拟类的示例:

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

为了确保测试中的模拟对象与它们要替换的对象具有相同的API,可以使用 auto-speccing . 可以通过 AutoPoC patch的参数,或 create_autospec() 功能。自动规范创建的模拟对象具有与其替换的对象相同的属性和方法,并且任何函数和方法(包括构造函数)都具有与真实对象相同的调用签名。

这样可以确保模拟在错误使用时会以与生产代码相同的方式失败:

>>> 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是可调用的,访问它们时创建属性作为新的mock 1. 访问相同的属性将始终返回相同的模拟。模拟记录了您如何使用它们,允许您对代码对它们所做的操作作出断言。

MagicMock is a subclass of Mock with all the magic methods pre-created and ready to use. There are also non-callable variants, useful when you are mocking out objects that aren't callable: NonCallableMock and NonCallableMagicMock

这个 patch() decorator使得用 Mock 对象。默认 patch() 将创建一个 MagicMock 为你。您可以指定 Mock 使用 new_callable 参数 patch() .

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(不包括不受支持的magic属性和方法)来形成字符串列表。访问不在此列表中的任何属性将引发 AttributeError .

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

  • spec_set :更严格的变体 spec . 如果使用,尝试 set 或者获取不在作为 spec_set 将提高 AttributeError .

  • side_effect :每当调用模拟时要调用的函数。见 side_effect 属性。用于引发异常或动态更改返回值。使用与mock相同的参数调用函数,除非它返回 DEFAULT ,此函数的返回值用作返回值。

    替代地 side_effect 可以是异常类或实例。在这种情况下,调用模拟时将引发异常。

    如果 side_effect 是ITerable,则对mock的每个调用都将从ITerable返回下一个值。

    A side_effect 可以通过将其设置为 None .

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

  • 不安全 :默认情况下,访问名称以 断言配件ASERT无人值守分类 将引发一个 AttributeError 。通过 unsafe=True 将允许访问这些属性。

    3.5 新版功能.

  • 包裹 :要封装模拟对象的项。如果 包裹 不是 None 然后调用mock将把调用传递给被封装的对象(返回实际结果)。mock上的属性访问将返回一个mock对象,该对象封装所封装对象的相应属性(因此尝试访问不存在的属性将引发 AttributeError

    如果模拟有明确的 return_value set then调用不会传递给封装的对象, return_value 而是返回。

  • name :如果模型有名称,则将在模型的报告中使用。这对于调试很有用。该名称将传播到子模拟。

模拟也可以用任意关键字参数调用。这些将用于在创建模拟后在其上设置属性。见 configure_mock() 详细信息的方法。

assert_called()

断言至少调用了一次模拟。

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

3.6 新版功能.

assert_called_once()

断言模拟调用了一次。

>>> 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(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)

断言已使用指定的参数调用了模拟。

如果模拟具有 ever 被称为,不像 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_calls 检查通话列表。

如果 any_order 如果为false,则调用必须是连续的。在指定的调用之前或之后可以有额外的调用。

如果 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()

断言从未调用模拟。

>>> 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)

重置模拟方法重置模拟对象上的所有调用属性:

>>> 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 . 子模拟和返回值模拟(如果有的话)也被重置。

注解

return_valueside_effect 只有关键字参数。

mock_add_spec(spec, spec_set=False)

向模型添加规范。 spec 可以是对象或字符串列表。只有上的属性 spec 可以从模拟中作为属性获取。

如果 spec_set 为true,则只能设置规范上的属性。

attach_mock(mock, attribute)

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

configure_mock(**kwargs)

在模拟关键字参数上设置属性。

属性加上返回值和副作用可以在子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

同样的事情也可以在构造函数调用mocks中实现:

>>> 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() 这样做是为了在创建模拟之后更容易进行配置。

__dir__()

Mock 对象限制的结果 dir(some_mock) 为了有用的结果。对于一个 spec 这包括模拟的所有允许属性。

FILTER_DIR 了解这个过滤的作用,以及如何关闭它。

_get_child_mock(**kw)

为属性和返回值创建子模拟。默认情况下,子模拟与父模拟的类型相同。mock的子类可能想要重写这个来定制子mock的生成方式。

对于不可调用模拟,将使用可调用变量(而不是任何自定义子类)。

called

表示是否调用模拟对象的布尔值:

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

一个整数,告诉您模拟对象被调用的次数:

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

设置此值以配置通过调用模拟返回的值:

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

默认返回值是一个模拟对象,您可以按常规方式配置它:

>>> 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

这可以是调用模拟时要调用的函数、可ITerable或要引发的异常(类或实例)。

如果传入一个函数,将使用与mock相同的参数调用它,除非函数返回 DEFAULT 对mock的调用将返回函数返回的任何内容。如果函数返回 DEFAULT 然后模拟将返回其正常值(从 return_value

如果传入一个iterable,它将用于检索迭代器,该迭代器必须在每次调用时生成一个值。该值可以是要引发的异常实例,也可以是从调用mock返回的值。 (DEFAULT 处理与功能案例相同)。

引发异常的模拟示例(用于测试API的异常处理):

>>> 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_effectNone 清除它:

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

这也不是 None (如果尚未调用模拟),或上次调用模拟时使用的参数。这将以元组的形式出现:第一个成员,也可以通过 args 属性,是用(或空元组)和第二个成员调用模拟的任何有序参数,也可以通过 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_listmethod_callsmock_callscall 物体。这些是元组,因此可以对它们进行解包,以获取单个参数并做出更复杂的断言。见 calls as tuples .

在 3.8 版更改: 补充 argskwargs 性质。

call_args_list

这是按顺序对模拟对象进行的所有调用的列表(因此列表的长度是调用的次数)。在进行任何调用之前,它是一个空列表。这个 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_listcall 物体。可以将这些解包为元组以获取各个参数。见 calls as tuples .

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_callscall 物体。可以将这些解包为元组以获取各个参数。见 calls as tuples .

mock_calls

mock_calls 记录 all 调用模拟对象、其方法、魔力方法 and 返回值模拟。

>>> 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_callscall 物体。可以将这些解包为元组以获取各个参数。见 calls as tuples .

注解

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__class__ 返回spec类。这允许模拟对象通过 isinstance() 测试他们正在替换/伪装的对象为:

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

__class__ 可分配给,这允许模拟通过 isinstance() 检查而不强制使用规范:

>>> 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 这对不可调用的模拟没有意义。

将类或实例用作 specspec_set 能够通过 isinstance() 测验:

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

这个 Mock 类支持模拟魔术方法。见 magic methods 详细信息。

模拟课和 patch() 修饰符都采用任意关键字参数进行配置。对于 patch() decorator将关键字传递给正在创建的模拟的构造函数。关键字参数用于配置模拟的属性:

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

子模拟的返回值和副作用可以用同样的方式设置,使用点符号。由于不能在调用中直接使用点式名称,因此必须创建字典并使用 **

>>> 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 )将在匹配对模拟的调用时自省规范对象的签名。因此,它可以匹配实际调用的参数,而不管它们是按位置传递的还是按名称传递的:

>>> 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() .什么时候? 自聚焦 它还将应用于模拟对象上的方法调用。

在 3.4 版更改: 在指定和自动指定的模拟对象上添加了签名自省。

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

拟用作类上的属性或其他描述符的模拟。 PropertyMock 提供 __get__()__set__() 方法,以便在提取时指定返回值。

取A PropertyMock 来自对象的实例调用模拟,不带参数。设置它调用具有所设置值的模拟。::

>>> 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)]

由于模拟属性的存储方式,您不能直接附加 PropertyMock 模拟对象。相反,您可以将其附加到模拟类型对象:

>>> 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 是iterable,async函数将返回iterable的下一个值,但是,如果结果序列已用完, StopAsyncIteration 立即升起,

  • 如果 side_effect 未定义,异步函数将返回 return_value 因此,在默认情况下,异步函数返回一个新的 AsyncMock 对象。

设置 spec A的 MockMagicMock 到异步函数将导致调用后返回协程对象。

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

设置 spec A的 MockMagicMockAsyncMock 对于具有异步和同步函数的类,将自动检测同步函数并将其设置为 MagicMock (如果父模拟是 AsyncMockMagicMockMock (如果父模拟是 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()

断言至少等待了一次模拟。注意,这与已调用的对象 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 = 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 = 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 = 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)

断言模拟已通过指定的调用等待。这个 await_args_list 检查列表是否有等待。

如果 any_order 如果为false,则等待必须是连续的。在指定的等待之前或之后可以有额外的呼叫。

如果 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 = AsyncMock()
>>> mock.assert_not_awaited()
reset_mock(*args, **kwargs)

Mock.reset_mock() . 同时设置 await_count 到0, await_args 到无,并清除 await_args_list .

await_count

一个整数,用于跟踪模拟对象等待的次数。

>>> 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.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 = 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')]

调用

模拟对象是可调用的。调用将返回设置为 return_value 属性。默认的返回值是一个新的模拟对象;它是在第一次访问返回值时创建的(显式地或通过调用模拟对象),但它是存储的,并且每次返回相同的值。

对对象的调用将记录在以下属性中 call_argscall_args_list .

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

使模拟在调用时引发异常的最简单方法是 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 是一个函数,那么该函数返回的是对模拟返回的调用。这个 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),或者任何设置的返回值,那么有两种方法可以做到这一点。要么返回 mock.return_value 从内部 side_effect 或返回 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_effectNone

>>> 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的重复调用将从iterable返回值(直到iterable耗尽并且 StopIteration 提出:

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

如果iterable的任何成员是异常,则将引发这些成员而不是返回::

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

删除属性

模拟对象根据需要创建属性。这允许它们假装成任何类型的对象。

您可能需要一个模拟对象返回 False 到A hasattr() 调用或提出 AttributeError 获取属性时。您可以通过将对象作为 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

模拟名称和名称属性

因为“name”是 Mock 构造函数,如果您希望模拟对象具有“name”属性,则不能在创建时将其传入。有两种选择。一种选择是使用 configure_mock() ::

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

一个更简单的选项是在模拟创建之后简单地设置“name”属性:

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

将模拟附加为属性

当您将一个mock附加为另一个mock的属性(或作为返回值)时,它将成为该mock的“子级”。对子项的调用记录在 method_callsmock_calls 父级的属性。这对于配置子模拟,然后将它们附加到父模拟,或将模拟附加到记录所有对子模拟的调用并允许您对模拟之间的调用顺序作出断言的父模拟很有用:

>>> 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 = 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() 自动命名。要将具有名称的模拟附加到父级,请使用 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

唯一的例外是magic方法和属性(那些具有前导和尾随双下划线的方法和属性)。mock不创建这些,而是引发 AttributeError . 这是因为解释器通常会隐式地请求这些方法,并 very 当一个新的模拟对象需要一个魔法方法时,它会感到困惑。如果您需要魔法方法支持,请参阅 magic methods .

补丁程序

补丁装饰器仅用于在对象装饰的功能范围内修补对象。即使出现异常,它们也会自动为您处理解锁。所有这些函数也可以在WITH语句中使用,或者作为类修饰符使用。

补丁

注解

关键是在正确的命名空间中进行修补。见章节 where to patch .

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

patch() 充当函数修饰器、类修饰器或上下文管理器。在函数体或WITH语句中, 目标 用一个 new 对象。当函数/WITH语句退出时,修补程序将撤消。

如果 new 忽略,则目标替换为 AsyncMock 如果修补对象是异步函数或 MagicMock 否则。如果 patch() 用作装饰和 new 如果省略,则创建的mock作为附加参数传递给修饰函数。如果 patch() 用作上下文管理器,创建的模拟由上下文管理器返回。

目标 应为窗体中的字符串 'package.module.ClassName' . 这个 目标 被导入,指定的对象替换为 new 对象,所以 目标 必须能够从您调用的环境中导入 patch() 从。目标在执行修饰函数时导入,而不是在修饰时导入。

这个 specspec_set 关键字参数传递给 MagicMock 如果补丁程序正在为您创建一个。

另外你可以通过 spec=Truespec_set=True ,这将导致修补程序传入被模拟为spec/spec_set对象的对象。

new_callable 允许您指定将被调用以创建 new 对象。默认 AsyncMock 用于异步函数和 MagicMock 剩下的。

一种更强大的 specAutoPoC . 如果你设置 autospec=True 然后将使用替换对象的规范创建模拟。模拟的所有属性也将具有被替换对象的相应属性的规范。模拟的方法和函数将检查其参数,并将引发 TypeError 如果用错误的签名调用他们。对于替换类的模拟,它们的返回值(“instance”)将具有与类相同的规范。见 create_autospec() 功能和 自聚焦 .

而不是 autospec=True 你可以通过 autospec=some_object 使用任意对象作为规范,而不是被替换的对象。

默认情况下 patch() 将无法替换不存在的属性。如果你通过 create=True 如果属性不存在,则在调用修补函数时,patch将为您创建该属性,并在修补函数退出后再次将其删除。这对于根据生产代码在运行时创建的属性编写测试很有用。默认情况下它是关闭的,因为它可能很危险。打开它之后,您就可以针对实际不存在的API编写通过的测试了!

注解

在 3.5 版更改: 如果您正在修补模块中的内置组件,则无需通过 create=True ,将默认添加。

补丁可以用作 TestCase 类修饰器。它通过修饰类中的每个测试方法来工作。当测试方法共享一个公共补丁集时,这会减少样板代码。 patch() 通过查找以开头的方法名查找测试 patch.TEST_PREFIX . 默认情况下,这是 'test' ,与之匹配 unittest 查找测试。您可以通过设置指定可选前缀 patch.TEST_PREFIX .

修补程序可以用作上下文管理器,带有WITH语句。这里的修补应用于WITH语句后面的缩进块。如果使用“as”,则修补后的对象将绑定到“as”后面的名称;如果 patch() 正在为您创建模拟对象。

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

patch.dict(...)patch.multiple(...)patch.object(...) 可用于其他用例。

patch() 作为函数修饰器,为您创建模拟并将其传递到修饰函数:

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

修补类将类替换为 MagicMock 实例 . 如果类在被测试的代码中被实例化,那么它将是 return_value 将要使用的模拟。

如果类被实例化多次,则可以使用 side_effect 每次返回一个新的模拟。或者,您可以设置 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_setpatch() 正在替换 classes ,则创建的模拟的返回值将具有相同的规范::

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

这个 new_callable 如果要使用默认的替代类,参数非常有用 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() 正在为您创建一个模拟,通常您需要做的第一件事是配置模拟。其中一些配置可以在调用修补程序时完成。您传递到调用中的任意关键字将用于在创建的模拟上设置属性::

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

以及创建的模拟属性上的属性,如 return_valueside_effect ,也可以配置子模拟的。这些作为关键字参数直接传递在语法上是无效的,但是使用这些作为关键字的字典仍然可以扩展为 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'

但加入 create=True 在召唤 patch() 将使前面的示例按预期工作:

>>> @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)

修补命名成员( 属性 )在一个物体上( 目标 )模拟物体。

patch.object() 可以用作修饰器、类修饰器或上下文管理器。参数 newspec创造spec_setAutoPoCnew_callable 含义与 patch() . 类似于 patch()patch.object() 采用任意关键字参数来配置它创建的模拟对象。

当用作类修饰器时 patch.object() 荣誉称号 patch.TEST_PREFIX 选择要封装的方法。

你可以打任何一个调用 patch.object() 有三个或两个参数。三参数形式包括要修补的对象、属性名和要用其替换属性的对象。

使用两个参数形式调用时,省略替换对象,并为您创建一个模拟,并作为附加参数传递给修饰函数:

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

spec创造 其他的参数 patch.object() 与它们的含义相同 patch() .

patch.dict

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

修补字典或类似字典的对象,并在测试后将字典恢复到原始状态。

in_dict 可以是字典或类似于映射的容器。如果它是一个映射,那么它必须至少支持获取、设置和删除项以及遍历键。

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

价值观 可以是要在字典中设置的值的字典。 价值观 也可以是 (key, value) 对。

如果 清楚的 如果为true,则在设置新值之前将清除字典。

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

在 3.8 版更改: patch.dict() 现在返回作为上下文管理器使用的修补字典。

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 . 有关如何更改see值的详细信息 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'):
    ...

使用 DEFAULT 如果你想要的话,作为价值 patch.multiple() 为您创建模拟。在这种情况下,创建的mock通过关键字传递到修饰函数,当 patch.multiple() 用作上下文管理器。

patch.multiple() 可以用作修饰器、类修饰器或上下文管理器。参数 specspec_set创造AutoPoCnew_callable 含义与 patch() . 这些参数将应用于 all 补丁通过 patch.multiple() .

当用作类修饰器时 patch.multiple() 荣誉称号 patch.TEST_PREFIX 选择要封装的方法。

如果你想要 patch.multiple() 要为您创建模拟,则可以使用 DEFAULT 作为价值。如果你使用 patch.multiple() 作为一个装饰器,然后创建的模拟通过关键字传递到装饰函数中。::

>>> 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() 方法。这些使修补变得更简单 setUp 方法或希望在不嵌套装饰器或不使用语句的情况下执行多个修补程序的位置。

使用它们调用 patch()patch.object()patch.dict() 和正常情况一样,并保持对返回的 patcher 对象。然后你可以调用 start() 把补丁放好 stop() 解开它。

如果您正在使用 patch() 若要为您创建模拟,则将通过调用返回到 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

这方面的一个典型用例可能是在 setUp A方法 TestCase ::

>>> 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应用装饰器的标准方法。传递到测试函数中的已创建模拟的顺序与此顺序匹配。

贴补何处

patch() 通过(临时)更改 name 指向另一个。可以有许多名称指向任何单个对象,因此要使修补工作正常,必须确保修补被测系统使用的名称。

基本原则是你修补一个物体的位置 抬起头 不一定与定义地点相同。几个例子将有助于澄清这一点。

假设我们有一个项目要用以下结构进行测试:

a.py
    -> Defines SomeClass

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

现在我们要测试 some_function 但我们想模拟一下 SomeClass 使用 patch() . 问题是,当我们导入模块B时,必须先导入模块B,然后再导入模块B SomeClass 来自模块A。如果我们使用 patch() 嘲弄 a.SomeClass 那么它对我们的测试没有影响;模块B已经引用了 real SomeClass 看起来我们的修补没有效果。

关键是修补 SomeClass 使用地点(或查找地点)。在这种情况下 some_function 会查到的 SomeClass 在模块B中,我们已经导入了它。修补应如下所示:

@patch('b.SomeClass')

但是,考虑另一种方案,其中 from a import SomeClass 模块B import asome_function 使用 a.SomeClass .这两种输入形式都很常见。在这种情况下,我们要修补的类在模块中被查找,因此我们必须修补 a.SomeClass 取而代之的是:

@patch('a.SomeClass')

修补描述符和代理对象

两个 patchpatch.object 正确地修补和恢复描述符:类方法、静态方法和属性。你应该把这些贴在 classes 而不是实例。他们也与 some 代理属性访问的对象,如 django settings object .

magicMock和magic方法支持

模仿魔术的方法

Mock 支持模拟Python协议方法,也称为“魔力方法”。这允许模拟对象替换容器或其他实现Python协议的对象。

因为魔术的方法和普通的方法是不同的 2, 这项支持已得到特别实施。这意味着只支持特定的魔力方法。支持的列表包括 几乎 所有这些。如果您需要什么,请告诉我们。

通过将感兴趣的方法设置为函数或模拟实例,可以模拟魔术方法。如果您使用的是函数,那么它 mustself 作为第一个参数 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)

对magic方法的调用不会出现在 method_calls ,但它们被记录在 mock_calls .

注解

如果您使用 spec 创建模拟的关键字参数,然后尝试设置不在规范中的magic方法将引发 AttributeError .

支持的魔术方法的完整列表是:

  • __hash__, __sizeof__, __repr__ and __str__

  • __dir__, __format__ and __subclasses__

  • __round__, __floor__, __trunc__ and __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____delete__

  • 腌渍: __reduce____reduce_ex____getinitargs____getnewargs____getstate____setstate__

  • 文件系统路径表示: __fspath__

  • 异步迭代方法: __aiter____anext__

在 3.8 版更改: 为添加了支持 os.PathLike.__fspath__() .

在 3.8 版更改: 为添加了支持 __aenter____aexit____aiter____anext__ .

以下方法存在,但 not 由于它们正在被mock使用,无法动态设置,或者可能导致问题:

  • __getattr__, __setattr__, __init__ and __new__

  • __prepare__, __instancecheck__, __subclasscheck__, __del__

魔术模拟

有两个 MagicMock 变体: MagicMockNonCallableMagicMock .

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

MagicMock 是的子类 Mock 使用大多数magic方法的默认实现。你可以用 MagicMock 不用自己配置魔法方法。

构造函数参数的含义与 Mock .

如果您使用 specspec_set 然后参数 only 将创建规范中存在的魔力方法。

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

不可调用的版本 MagicMock .

构造函数参数的含义与 MagicMock ,除了 return_valueside_effect 这对不可调用的模拟没有意义。

魔法方法是用 MagicMock 对象,以便您可以按常规方式配置和使用它们:

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

默认情况下,返回特定类型的对象需要许多协议方法。这些方法预先配置了一个默认的返回值,这样,如果您对返回值不感兴趣,可以不用做任何事情就可以使用它们。你仍然可以 set 如果要更改默认值,则手动返回值。

方法及其默认值:

  • __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__ :模拟的默认hash

  • __str__ :模拟的默认str

  • __sizeof__ :模拟的默认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__() 可以是任何可Iterable对象,不需要是迭代器:

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

如果返回值 is 一个迭代器,然后在它上面迭代一次,将使用它,随后的迭代将导致一个空列表:

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

MagicMock 除了一些模糊和过时的方法外,配置了所有支持的魔力方法。如果你想的话,你仍然可以设置这些。

默认情况下不支持的Magic方法 MagicMock 是:

  • __subclasses__

  • __dir__

  • __format__

  • __get__, __set__ and __delete__

  • __reversed__ and __missing__

  • __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate__ and __setstate__

  • __getformat__ and __setformat__

2

魔术方法 应该 在类上而不是实例上查找。关于应用此规则,不同版本的python不一致。支持的协议方法应该与所有支持的Python版本一起工作。

3

函数基本上与类挂钩,但是 Mock 实例与其他实例保持隔离。

帮手

哨兵

unittest.mock.sentinel

这个 sentinel 对象提供了为测试提供独特对象的方便方法。

当您按名称访问属性时,它们是按需创建的。访问同一属性将始终返回同一对象。返回的对象具有合理的repr,因此测试失败消息是可读的。

在 3.7 版更改: 这个 sentinel 属性现在保留它们的身份 copiedpickled .

有时,当测试时,您需要测试特定对象是否作为参数传递给另一个方法,或者返回。创建命名的sentinel对象来测试这一点很常见。 sentinel 提供了一种创建和测试类似对象标识的方便方法。

在这个例子中,我们猴子补丁 method 归来 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
>>> sentinel.some_object
sentinel.some_object

DEFAULT

unittest.mock.DEFAULT

这个 DEFAULT 对象是预先创建的sentinel(实际上 sentinel.DEFAULT )它可以被 side_effect 用于指示应使用正常返回值的函数。

调用

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

call() 是用于生成简单断言的辅助对象,用于与 call_argscall_args_listmock_callsmethod_calls . call() 也可用于 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()

对于表示多个调用的调用对象, call_list() 返回所有中间调用和最终调用的列表。

call_list 对于对“链接调用”进行断言特别有用。链接调用是在一行代码上进行的多个调用。这将导致在 mock_calls 嘲弄手动构造调用序列可能很麻烦。

call_list() 无法从同一个链接调用构造调用序列:

>>> 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 对象是(位置参数、关键字参数)或(名称、位置参数、关键字参数)的元组,具体取决于其构造方式。当你自己构建它们的时候,这并不特别有趣,但是 call 中的对象 Mock.call_argsMock.call_args_listMock.mock_calls 可以对属性进行内省,以获得它们所包含的各个参数。

这个 call 中的对象 Mock.call_argsMock.call_args_list 是(位置参数、关键字参数)的两个元组,而 call 中的对象 Mock.mock_calls 与您自己构造的元组一起,有三个元组(name、positional args、keyword args)。

您可以使用它们的“tupesity”为更复杂的内省和断言提取单独的参数。位置参数是一个元组(如果没有位置参数,则为空元组),关键字参数是一个字典:

>>> 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)

使用另一个对象作为规范创建模拟对象。模拟上的属性将使用 spec 对象作为其规范。

被模拟的函数或方法将检查其参数,以确保使用正确的签名调用它们。

如果 spec_setTrue 然后尝试设置spec对象上不存在的属性将引发 AttributeError .

如果一个类被用作规范,那么mock(类的实例)的返回值将具有相同的规范。通过传递 instance=True . 仅当模拟的实例可调用时,返回的模拟才可调用。

create_autospec() 还接受传递给所创建模拟的构造函数的任意关键字参数。

自聚焦 有关如何使用自动规格化的示例 create_autospec() 以及 AutoPoC 参数 patch() .

在 3.8 版更改: create_autospec() 现在返回一个 AsyncMock 如果目标是异步函数。

ANY

unittest.mock.ANY

有时你可能需要对 some 调用mock时的参数,但要么不关心某些参数,要么希望将它们从 call_args 对它们做出更复杂的断言。

若要忽略某些参数,可以传入比较等于 一切 . 呼吁 assert_called_with()assert_called_once_with() 然后,不管传入什么,都会成功。

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

ANY 也可用于与调用列表进行比较,如 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 是控制模拟对象响应方式的模块级变量 dir() (仅适用于python 2.6或更高版本)。默认值为 True 它使用下面描述的筛选,只显示有用的成员。如果您不类似于此筛选,或者需要出于诊断目的将其关闭,请设置 mock.FILTER_DIR = False .

过滤打开时, dir(some_mock) 只显示有用的属性,并将包括通常不会显示的任何动态创建的属性。如果模型是用 spec (或) AutoPoC 当然)然后显示原始属性的所有属性,即使它们尚未被访问:

>>> 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',
 ...

很多不是很有用的(私人的 Mock 而不是模拟的东西)下划线和双下划线前缀属性已从调用的结果中筛选出来。 dir() 在一 Mock .如果您不类似于这种行为,可以通过设置模块级开关将其关闭。 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__',
 ...

或者你也可以用 vars(my_mock) (实例成员)和 dir(type(my_mock)) (类型成员)忽略筛选 mock.FILTER_DIR .

mock_open

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

用于创建模拟以替换 open() . 它适用于 open() 直接调用或用作上下文管理器。

这个 mock 参数是要配置的模拟对象。如果 None (默认)然后 MagicMock 将为您创建,API仅限于标准文件句柄上可用的方法或属性。

read_data 是一个字符串 read()readline()readlines() 要返回的文件句柄的方法。对这些方法的调用将从 read_data 直到耗尽。这些方法的模拟相当简单:每次 mock 被称为 read_data 重新回到起点。如果您需要更多的控制您正在向测试代码提供的数据,您将需要为自己定制这个模拟。当这还不够时,内存中的一个文件系统包 PyPI 可以为测试提供一个真实的文件系统。

在 3.4 版更改: 补充 readline()readlines() 支持。嘲弄 read() 改为消费 read_data 而不是每次通话都返回。

在 3.5 版更改: read_data 现在在每次调用时重置为 mock .

在 3.8 版更改: 补充 __iter__() 使迭代(如for循环)正确地消耗 read_data .

使用 open() 作为一个上下文管理器,是确保文件句柄正确关闭的一个很好的方法,并且越来越常见:

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

问题是,即使你假装调用给 open() 它是 返回对象 用作上下文管理器的 __enter__()__exit__() 叫)

模拟上下文管理器 MagicMock 足够普通和精细,帮助函数是有用的。::

>>> 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')

对于读取文件:

>>> 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'

自聚焦

自动排序基于现有 spec 模拟的特征。它将mock的api限制为原始对象(spec)的api,但它是递归的(延迟实现的),因此mock的属性与spec的属性只有相同的api。此外,mock的函数/方法与原始函数/方法具有相同的调用签名,因此它们引发了 TypeError 如果调用错误。

在我解释自动规范如何工作之前,这里是需要它的原因。

Mock 是一个非常强大和灵活的对象,但当用于模拟测试系统中的对象时,它会遇到两个缺陷。其中一个缺陷是特定于 Mock API和另一个更常见的问题是使用模拟对象。

首先是特定于 Mock . Mock 有两个非常方便的断言方法: assert_called_with()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.

因为mocks会根据需要自动创建属性,并允许您使用任意参数调用它们,如果您拼写错误了这些断言方法之一,那么您的断言就消失了:

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

由于打字错误,您的测试可能会无声且错误地通过。

第二个问题对嘲弄更为普遍。如果重构某些代码、重命名成员等,则对仍在使用 旧API 但使用模拟而不是真正的对象仍然会通过。这意味着即使您的代码被破坏,您的测试也可以全部通过。

请注意,这是您需要集成测试和单元测试的另一个原因。单独测试所有的东西都很好,而且很花哨,但是如果你不测试你的单元是如何“连接在一起”的,那么测试中仍然有很多可能会发现的错误空间。

mock 已经提供了一个功能来帮助实现这一点,称为speccing。如果使用类或实例作为 spec 对于模拟,则只能访问实体类上存在的模拟的属性:

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

规范只适用于模拟本身,因此我们仍然对模拟上的任何方法有相同的问题:

>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with()

自动规范解决了这个问题。你也可以通过 autospec=Truepatch() / patch.object() 或者使用 create_autospec() 函数创建具有规范的模拟。如果使用 autospec=True 参数 patch() 然后将替换的对象用作spec对象。因为规范化是“懒洋洋地”完成的(规范是作为对mock上的属性进行访问而创建的),所以您可以将它与非常复杂或深度嵌套的对象(如导入导入模块的模块)一起使用,而不会对性能造成重大影响。

下面是一个使用中的示例:

>>> 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='...'>

你可以看到 request.Request 有规格 request.Request 在构造函数中接受两个参数(其中一个是 self )如果我们不正确地称呼它,会发生以下情况:

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

规范还适用于实例化类(即规范化mock的返回值)::

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

Request 对象不可调用,因此实例化模拟对象的返回值 request.Request 是不可调用的模拟。规范就位后,断言中的任何拼写错误都将引发正确的错误::

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

在许多情况下,您可以添加 autospec=True 到你现有的 patch() 调用,然后防止由于拼写错误和API更改而导致的错误。

以及使用 AutoPoC 通过 patch() 有一个 create_autospec() 对于直接创建自动指定的模拟:

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

然而,这并非没有警告和限制,这就是为什么它不是默认行为。为了知道spec对象上可用的属性,autospec必须对spec进行内省(访问属性)。当您在mock上遍历属性时,原始对象的相应遍历将发生在hood下。如果任何指定的对象具有可以触发代码执行的属性或描述符,那么您可能无法使用autospec。另一方面,更好的方法是设计你的对象,这样内省是安全的。 4.

更严重的问题是,在 __init__() 方法,并且根本不存在于类中。 AutoPoC 无法了解任何动态创建的属性,并将API限制为可见属性。地址:

>>> 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'

有几种不同的方法来解决这个问题。最简单但不一定是最麻烦的方法是在创建后简单地在模拟上设置所需的属性。只是因为 AutoPoC 不允许您获取规范中不存在的属性,但不会阻止您设置这些属性:

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

两者都有更激进的版本 specAutoPoC 那个 does 防止设置不存在的属性。如果您只想确保您的代码 sets 同样有效的属性,但显然它阻止了这种特定的场景:

>>> 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'

解决这个问题的最好方法可能是添加类属性作为初始化实例成员的默认值。 __init__() . 请注意,如果只在中设置默认属性 __init__() 然后通过类属性(当然是在实例之间共享)提供它们也会更快。例如

class Something:
    a = 33

这带来了另一个问题。提供默认值是比较常见的 None 对于以后将成为不同类型对象的成员。 None 作为一个规范是没用的,因为它不允许你访问 any 属性或方法。AS None从未 作为规范很有用,并且可能指示通常属于其他类型的成员,autospec不为设置为的成员使用规范。 None .这些将只是普通的模拟(嗯-魔术师模拟):

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

如果修改生产类以添加默认值不符合您的喜好,那么还有更多的选项。其中之一就是使用实例作为规范,而不是类。另一种方法是创建生产类的子类,并在不影响生产类的情况下将默认值添加到子类中。谢天谢地,这两种方法都要求您使用另一个对象作为规范。 patch() 支持这一点-您可以简单地将替代对象作为 AutoPoC 参数:

>>> 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

这只适用于类或已经实例化的对象。调用模拟类以创建模拟实例 创建真实实例。它只是属性查找-以及对 dir() -完成了。

密封模拟

unittest.mock.seal(mock)

当访问被密封的模拟的属性或已经递归模拟的任何属性时,seal将禁用模拟的自动创建。

如果一个具有名称或规格的模拟实例被分配给一个属性,那么它将不会在密封链中被考虑。这样可以防止密封件固定模拟对象的一部分。::

>>> 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 新版功能.