unittest.mock
---模拟对象库¶
3.3 新版功能.
源代码: Lib/unittest/mock.py
unittest.mock
是一个在Python中测试的库。它允许您用模拟对象替换测试中的系统部分,并对它们的使用方式作出断言。
unittest.mock
提供核心 Mock
类删除了在整个测试套件中创建大量存根的需要。在执行操作之后,您可以断言使用了哪些方法/属性以及调用它们的参数。您还可以指定返回值并以正常方式设置所需的属性。
此外,mock还提供了 patch()
在测试范围内处理修补模块和类级属性的装饰器,以及 sentinel
用于创建唯一对象。查看 quick guide 例如如何使用 Mock
, MagicMock
和 patch()
.
mock非常容易使用,设计用于 unittest
. 模拟是基于“操作->断言”模式,而不是许多模拟框架使用的“记录->重播”。
有一个反向端口 unittest.mock
对于早期版本的python,可用作 mock on PyPI .
快速指南¶
Mock
和 MagicMock
对象在您访问它们时创建所有属性和方法,并存储它们的使用细节。您可以配置它们,指定返回值或限制哪些属性可用,然后就它们的使用方式作出断言:
>>> 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_value 或side_effect
,然后将相应的参数传递为True
. 子模拟和返回值模拟(如果有的话)也被重置。注解
return_value 和
side_effect
只有关键字参数。
- mock_add_spec(spec, spec_set=False)¶
向模型添加规范。 spec 可以是对象或字符串列表。只有上的属性 spec 可以从模拟中作为属性获取。
如果 spec_set 为true,则只能设置规范上的属性。
- attach_mock(mock, attribute)¶
附加一个mock作为这个mock的属性,替换它的名称和父级。对所附模型的调用将记录在
method_calls
和mock_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_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
(如果尚未调用模拟),或上次调用模拟时使用的参数。这将以元组的形式出现:第一个成员,也可以通过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_list
,method_calls
和mock_calls
是call
物体。这些是元组,因此可以对它们进行解包,以获取单个参数并做出更复杂的断言。见 calls as tuples .在 3.8 版更改: 补充
args
和kwargs
性质。
- 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_list
是call
物体。可以将这些解包为元组以获取各个参数。见 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_calls
是call
物体。可以将这些解包为元组以获取各个参数。见 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_calls
是call
物体。可以将这些解包为元组以获取各个参数。见 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_value 和 side_effect 这对不可调用的模拟没有意义。
将类或实例用作 spec
或 spec_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_effect
或return_value
在等待之后:如果
side_effect
是一个函数,异步函数将返回该函数的结果,如果
side_effect
是异常,异步函数将引发异常,如果
side_effect
是iterable,async函数将返回iterable的下一个值,但是,如果结果序列已用完,StopAsyncIteration
立即升起,如果
side_effect
未定义,异步函数将返回return_value
因此,在默认情况下,异步函数返回一个新的AsyncMock
对象。
设置 spec A的
Mock
或MagicMock
到异步函数将导致调用后返回协程对象。>>> async def async_func(): pass ... >>> mock = MagicMock(async_func) >>> mock <MagicMock spec='function' id='...'> >>> mock() <coroutine object AsyncMockMixin._mock_call at ...>
设置 spec A的
Mock
,MagicMock
或AsyncMock
对于具有异步和同步函数的类,将自动检测同步函数并将其设置为MagicMock
(如果父模拟是AsyncMock
或MagicMock
或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()¶
断言至少等待了一次模拟。注意,这与已调用的对象
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_args
和 call_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_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的重复调用将从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_calls
和 mock_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()
从。目标在执行修饰函数时导入,而不是在修饰时导入。这个 spec 和 spec_set 关键字参数传递给
MagicMock
如果补丁程序正在为您创建一个。另外你可以通过
spec=True
或spec_set=True
,这将导致修补程序传入被模拟为spec/spec_set对象的对象。new_callable 允许您指定将被调用以创建 new 对象。默认
AsyncMock
用于异步函数和MagicMock
剩下的。一种更强大的 spec 是 AutoPoC . 如果你设置
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'
...
如果你使用 spec 或 spec_set 和 patch()
正在替换 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_value
和 side_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()
patch.object¶
- patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶
修补命名成员( 属性 )在一个物体上( 目标 )模拟物体。
patch.object()
可以用作修饰器、类修饰器或上下文管理器。参数 new , spec , 创造 , spec_set , AutoPoC 和 new_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()
可以用作修饰器、类修饰器或上下文管理器。参数 spec , spec_set , 创造 , AutoPoC 和 new_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 a
和 some_function
使用 a.SomeClass
.这两种输入形式都很常见。在这种情况下,我们要修补的类在模块中被查找,因此我们必须修补 a.SomeClass
取而代之的是:
@patch('a.SomeClass')
修补描述符和代理对象¶
两个 patch 和 patch.object 正确地修补和恢复描述符:类方法、静态方法和属性。你应该把这些贴在 classes 而不是实例。他们也与 some 代理属性访问的对象,如 django settings object .
magicMock和magic方法支持¶
模仿魔术的方法¶
Mock
支持模拟Python协议方法,也称为“魔力方法”。这允许模拟对象替换容器或其他实现Python协议的对象。
因为魔术的方法和普通的方法是不同的 2, 这项支持已得到特别实施。这意味着只支持特定的魔力方法。支持的列表包括 几乎 所有这些。如果您需要什么,请告诉我们。
通过将感兴趣的方法设置为函数或模拟实例,可以模拟魔术方法。如果您使用的是函数,那么它 must 拿 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)
对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
变体: MagicMock
和 NonCallableMagicMock
.
- class unittest.mock.MagicMock(*args, **kw)¶
MagicMock
是的子类Mock
使用大多数magic方法的默认实现。你可以用MagicMock
不用自己配置魔法方法。构造函数参数的含义与
Mock
.如果您使用 spec 或 spec_set 然后参数 only 将创建规范中存在的魔力方法。
- class unittest.mock.NonCallableMagicMock(*args, **kw)¶
不可调用的版本
MagicMock
.构造函数参数的含义与
MagicMock
,除了 return_value 和 side_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__
帮手¶
哨兵¶
- unittest.mock.sentinel¶
这个
sentinel
对象提供了为测试提供独特对象的方便方法。当您按名称访问属性时,它们是按需创建的。访问同一属性将始终返回同一对象。返回的对象具有合理的repr,因此测试失败消息是可读的。
有时,当测试时,您需要测试特定对象是否作为参数传递给另一个方法,或者返回。创建命名的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_args
,call_args_list
,mock_calls
和method_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_args
, Mock.call_args_list
和 Mock.mock_calls
可以对属性进行内省,以获得它们所包含的各个参数。
这个 call
中的对象 Mock.call_args
和 Mock.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_set 是
True
然后尝试设置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=True
到 patch()
/ 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
...
两者都有更激进的版本 spec 和 AutoPoC 那个 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='...'>
密封模拟¶
- 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 新版功能.