26.5. unittest.mock
- mock对象库¶
版本3.3中的新功能。
unittest.mock
是用于在Python中测试的库。它允许您使用模拟对象替换测试中的系统部件,并对其使用方式进行断言。
unittest.mock
提供了一个核心Mock
类,无需在整个测试套件中创建多个存根。执行一个动作后,你可以断言使用了哪些方法/属性以及它们调用的参数。您还可以以正常方式指定返回值并设置所需的属性。
此外,mock提供了一个patch()
装饰器,用于处理测试范围内的修补模块和类级属性,以及用于创建唯一对象的sentinel
。有关如何使用Mock
,MagicMock
和patch()
的一些示例,请参阅快速指南。
Mock非常易于使用,设计用于unittest
。Mock基于“action - > assertion”模式,而不是许多模拟框架使用的“record - > replay”。
对于早期版本的Python,存在unittest.mock
的反向端口,可作为在PyPI上使用。
26.5.1. Quick Guide¶
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参数配置模拟从另一个对象获取其规范。尝试访问规范中不存在的模型上的属性或方法将失败,并出现AttributeError
。
patch()
装饰器/上下文管理器可以轻松地模拟被测模块中的类或对象。您在测试期间指定的对象将被替换为模拟(或其他对象),并在测试结束时恢复:
>>> 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()
注意
当你嵌套补丁修饰符时,mock会以它们应用的相同顺序传递到修饰函数中(应用修饰符的正常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实例)分配给魔术方法,它们将被适当地调用。MagicMock
类只是一个Mock变体,它具有为您预先创建的所有魔法方法(以及所有有用的方法)。
以下是使用魔术方法与普通Mock类的示例:
>>> mock = Mock()
>>> mock.__str__ = Mock(return_value='wheeeeee')
>>> str(mock)
'wheeeeee'
For ensuring that the mock objects in your tests have the same api as the objects they are replacing, you can use auto-speccing. 自动指定可以通过patch的autospec参数或create_autospec()
函数完成。自动指定创建具有与它们替换的对象相同的属性和方法的模拟对象,并且任何函数和方法(包括构造函数)具有与真实对象相同的调用声明。
这可以确保如果不正确地使用您的mock,则会以与生产代码相同的方式失败:
>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
... pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
...
TypeError: <lambda>() takes exactly 3 arguments (1 given)
create_autospec()
也可以在类上使用,其中它复制__init__
方法的声明以及在其中复制__call__
26.5.2. The Mock Class¶
Mock
是一个灵活的模拟对象,旨在替换您的代码中使用存根和测试双打。当您访问[1]时,模拟是可调用的并创建属性作为新的模拟。访问相同的属性将总是返回相同的模拟。Mocks记录你如何使用它们,使你能够断言你的代码对他们做了什么。
MagicMock
是预先创建并准备好使用的所有魔法方法的Mock
的子类。还有不可调用的变体,当你模拟不可调用的对象时非常有用:NonCallableMock
和NonCallableMagicMock
patch()
装饰器可以轻松地用Mock
对象临时替换特定模块中的类。默认情况下,patch()
会为您创建一个MagicMock
。您可以使用patch()
的new_callable参数指定Mock
的替代类。
- class
unittest.mock.
Mock
(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)¶ 创建一个新的
Mock
对象。Mock
使用几个指定Mock对象行为的可选参数:spec:这可以是字符串列表或作为模拟对象的规范的现有对象(类或实例)。如果你传递一个对象,然后通过调用对象的dir(排除不支持的魔法属性和方法)形成一个字符串列表。访问不在此列表中的任何属性将引发
AttributeError
。如果spec是一个对象(而不是字符串列表),则
__class__
返回spec对象的类。这允许mock通过isinstance()
测试。spec_set:规格的更严格的变体。如果使用,尝试设置或在模拟上获取不在作为spec_set传递的对象上的属性将引发
AttributeError
。side_effect:调用Mock时调用的函数。请参阅
side_effect
属性。用于提高异常或动态更改返回值。该函数使用与mock相同的参数调用,除非返回DEFAULT
,此函数的返回值将用作返回值。或者,side_effect可以是异常类或实例。在这种情况下,当调用模拟时将引发异常。
如果side_effect是可迭代的,那么每次调用mock将返回可迭代的下一个值。
A side_effect可以通过将其设置为
None
来清除。return_value:调用模拟时返回的值。默认情况下,这是一个新的Mock(在第一次访问时创建)。请参见
return_value
属性。unsafe:如果任何属性以assert或assret开头,则会引发
AttributeError
。传递unsafe=True
将允许访问这些属性。版本3.5中的新功能。
wraps:要包装的模拟对象的项目。如果wraps不是None,那么调用Mock会将调用传递给包装的对象(返回真实结果)。对模拟的属性访问将返回一个Mock对象,它包装了包装对象的相应属性(因此试图访问一个不存在的属性将引发一个
AttributeError
)。如果模拟具有显式的return_value集合,那么调用不会传递到包装的对象,而是返回return_value。
name:如果模拟有一个名称,那么它将被用于模拟的repr。这可以用于调试。该名称将传播到子模型。
Mocks也可以使用任意关键字参数调用。这些将用于在创建模拟后设置属性。有关详细信息,请参阅
configure_mock()
方法。-
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('foo', bar='baz') >>> mock.assert_called_once_with('foo', bar='baz') Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times.
-
assert_any_call
(*args, **kwargs)¶ 断言模拟已使用指定的参数调用。
如果模拟过被调用,则assert通过,与
assert_called_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
()¶ reset_mock方法重置一个mock对象上的所有调用属性:
>>> mock = Mock(return_value=None) >>> mock('hello') >>> mock.called True >>> mock.reset_mock() >>> mock.called False
这在您想要重新使用相同对象的一系列断言时可能很有用。请注意,
reset_mock()
不会清除返回值side_effect
或使用正常分配设置的任何子属性。子模块和返回值模拟(如果有)也被重置。
-
mock_add_spec
(spec, spec_set=False)¶ 添加规范到模拟。spec可以是对象或字符串列表。只有规格上的属性才能从模拟中提取为属性。
如果spec_set为true,那么只能设置spec上的属性。
-
attach_mock
(mock, attribute)¶ 将模拟作为此属性的一个属性,替换其名称和父级。对附加模拟的调用将记录在此类的
method_calls
和mock_calls
属性中。
-
configure_mock
(**kwargs)¶ 通过关键字参数在模拟上设置属性。
属性加上返回值和副作用可以使用标准点符号在子模型上设置,并在方法调用中分离一个字典:
>>> mock = Mock() >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock.configure_mock(**attrs) >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyError
同样的事情可以在mock的构造函数调用中实现:
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock = Mock(some_attribute='eggs', **attrs) >>> mock.some_attribute 'eggs' >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyError
configure_mock()
,以便在创建模拟后更容易进行配置。
-
__dir__
()¶ Mock
对象将dir(some_mock)
的结果限制为有用的结果。对于具有spec的模拟,这包括模拟的所有允许的属性。有关此过滤的操作以及如何将其关闭,请参见
FILTER_DIR
。
-
_get_child_mock
(**kw)¶ 为属性和返回值创建子模型。默认情况下,子模板与父模型的类型相同。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() >>> mock.return_value = 'fish' >>> mock() 'fish'
默认返回值是一个mock对象,你可以配置它以正常的方式:
>>> mock = Mock() >>> mock.return_value.attribute = sentinel.Attribute >>> mock.return_value() <Mock name='mock()()' id='...'> >>> mock.return_value.assert_called_with()
return_value
也可以在构造函数中设置:>>> mock = Mock(return_value=3) >>> mock.return_value 3 >>> mock() 3
-
side_effect
¶ 这可以是在调用模拟时调用的函数,可迭代的或异常(类或实例)。
如果你传递一个函数,它会使用和mock相同的参数来调用,除非函数返回
DEFAULT
单例,对mock的调用将返回函数返回的任何函数。如果函数返回DEFAULT
,那么模拟将返回其正常值(从return_value
)。如果你传递一个迭代器,它被用于检索一个迭代器,它必须在每次调用产生一个值。该值可以是要引发的异常实例,也可以是从调用模拟(
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)
使用callable:
>>> mock = Mock(return_value=3) >>> def side_effect(*args, **kwargs): ... return DEFAULT ... >>> mock.side_effect = side_effect >>> mock() 3
side_effect
可以在构造函数中设置。这里有一个例子,它添加一个值为mock被调用,并返回它:>>> side_effect = lambda value: value + 1 >>> mock = Mock(side_effect=side_effect) >>> mock(3) 4 >>> mock(-8) -7
将
side_effect
设置为None
:清除它:>>> m = Mock(side_effect=KeyError, return_value=3) >>> m() Traceback (most recent call last): ... KeyError >>> m.side_effect = None >>> m() 3
-
call_args
¶ 这是
None
(如果没有调用模拟),或者模拟最后调用的参数。这将是一个元组的形式:第一个成员是使用(或一个空元组)调用mock的任何有序参数,第二个成员是任何关键字参数(或空字典)。>>> mock = 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(3, 4, 5, key='fish', next='w00t!') >>> mock.call_args call(3, 4, 5, key='fish', next='w00t!')
call_args
以及列表call_args_list
,method_calls
和mock_calls
的成员都是call
对象。这些是元组,所以他们可以解包来获得单个参数,并进行更复杂的断言。请参见calls as tuples。
-
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
记录所有对模拟对象的调用,其方法,魔术方法和返回值模拟。>>> 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。
-
__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
的不可调用版本。除了return_value和side_effect,构造函数参数具有与Mock
相同的含义,它们对不可调用的模拟没有意义。
使用类或实例作为spec
或spec_set
的Mock对象能够通过isinstance()
测试:
>>> mock = Mock(spec=SomeClass)
>>> isinstance(mock, SomeClass)
True
>>> mock = Mock(spec_set=SomeClass())
>>> isinstance(mock, SomeClass)
True
Mock
类支持模拟魔术方法。有关详细信息,请参阅magic methods。
模拟类和patch()
装饰器都采用任意关键字参数进行配置。对于patch()
装饰器,将关键字传递给正在创建的模拟器的构造函数。关键字参数用于配置模拟的属性:
>>> 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()
。当Autospeccing时,它也将应用于模拟对象上的方法调用。
Changed in version 3.4: Added signature introspection on specced and autospecced mock objects.
- class
unittest.mock.
PropertyMock
(*args, **kwargs)¶ 模拟打算用作一个类的属性或其他描述器。
PropertyMock
提供__get__()
和__set__()
方法,以便您可以在获取时指定返回值。从对象获取
PropertyMock
实例调用模拟,没有args。设置它调用模拟与设置的值。>>> 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
附加到模拟对象。相反,您可以将其附加到mock类型对象:
>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> type(m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()
26.5.2.1. Calling¶
模拟对象是可调用的。调用将返回设置为return_value
属性的值。默认返回值是一个新的Mock对象;它是在第一次访问返回值(显式地或通过调用Mock)时创建的 - 但是它被存储并且每次返回相同的值。
对对象的调用将记录在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
是一个函数,那么该函数返回的是对mock return的调用。使用与模拟相同的参数调用side_effect
函数。这允许您基于输入动态地改变调用的返回值:
>>> 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)]
如果你想让模拟器仍然返回默认返回值(一个新的模拟)或任何设置的返回值,那么有两种方法。从side_effect
内返回mock.return_value
,或返回DEFAULT
:
>>> m = MagicMock()
>>> def side_effect(*args, **kwargs):
... return m.return_value
...
>>> m.side_effect = side_effect
>>> m.return_value = 3
>>> m()
3
>>> def side_effect(*args, **kwargs):
... return DEFAULT
...
>>> m.side_effect = side_effect
>>> m()
3
要删除side_effect
并返回默认行为,请将side_effect
设置为None
:
>>> m = MagicMock(return_value=6)
>>> def side_effect(*args, **kwargs):
... return 3
...
>>> m.side_effect = side_effect
>>> m()
3
>>> m.side_effect = None
>>> m()
6
side_effect
也可以是任何可迭代对象。对mock的重复调用将从迭代器返回值(直到迭代器耗尽并且产生StopIteration
):
>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
...
StopIteration
如果iterable的任何成员是异常,它们将被抛出而不是返回:
>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
...
ValueError
>>> m()
66
26.5.2.2. Deleting Attributes¶
Mock对象根据需要创建属性。这允许他们假装成任何类型的对象。
您可能需要一个mock对象在获取属性时将False
返回到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
26.5.2.3. Mock names and the name attribute¶
因为“name”是Mock
构造函数的参数,如果你想要你的mock对象有一个“name”属性,你不能只是在创建时传递它。有两种选择。一个选项是使用configure_mock()
:
>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'
一个更简单的选项是在模拟创建后简单地设置“name”属性:
>>> mock = MagicMock()
>>> mock.name = "foo"
26.5.2.4. Attaching Mocks as Attributes¶
当你将一个模拟作为另一个模拟的属性(或作为返回值),它成为那个模拟的“孩子”。对子进程的调用记录在父进程的method_calls
和mock_calls
属性中。这对于配置子模型,然后将它们附加到父模型,或者将mock附加到记录对子对象的所有调用的父对象,并允许您对mock之间的调用顺序进行断言:
>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]
例外情况是,如果模拟有一个名称。这允许你防止“育儿”,如果由于某种原因,你不想它发生。
>>> mock = MagicMock()
>>> not_a_child = MagicMock(name='not-a-child')
>>> mock.attribute = not_a_child
>>> mock.attribute()
<MagicMock name='not-a-child()' id='...'>
>>> mock.mock_calls
[]
由patch()
为您创建的模拟会自动给定名称。要附加具有父名称的mock,请使用attach_mock()
方法:
>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
... with patch('__main__.thing2', return_value=None) as child2:
... parent.attach_mock(child1, 'child1')
... parent.attach_mock(child2, 'child2')
... child1('one')
... child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]
[1] | 唯一的例外是魔法方法和属性(具有前导和尾部双下划线的方法和属性)。Mock不创建这些,而是引入AttributeError 。这是因为解释器通常会隐式地请求这些方法,并且当需要一个魔术方法时,非常会混淆得到一个新的Mock对象。如果您需要魔法方法支持,请参阅magic methods。 |
26.5.3. The patchers¶
补丁装饰器仅用于在它们装饰的函数的范围内修补对象。即使出现异常,它们也会自动处理解压缩。所有这些函数也可以用于语句或类装饰器。
26.5.3.1. patch ¶
-
unittest.mock.
patch
(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ patch()
充当函数装饰器,类装饰器或上下文管理器。在函数或with语句的正文中,目标用新对象打补丁。当函数/ with语句退出时,补丁被取消。如果省略new,则目标将替换为
MagicMock
。如果patch()
用作装饰器件并且省略new,则创建的mock作为附加参数传递到修饰函数。如果patch()
用作上下文管理器,则上下文管理器返回所创建的mock。target应为
'package.module.ClassName'
形式的字符串。导入目标且指定的对象替换为新对象,因此目标必须可从您调用的环境中导入patch()
。当执行装饰功能时,不是在装饰时导入目标。如果修补程序正在为您创建一个,则spec t>和spec_set关键字参数会传递到
MagicMock
。此外,您可以传递
spec=True
或spec_set=True
,这会导致补丁在被嘲笑为spec / spec_set对象的对象中传递。new_callable允许您指定一个不同的类或可调用对象,将被调用以创建新对象。默认使用
MagicMock
。spec的更强大的形式是autospec。如果设置
autospec=True
,那么将使用要替换的对象的spec来创建模拟。模拟的所有属性也将具有被替换的对象的相应属性的spec。被模拟的方法和函数将检查它们的参数,并且如果它们被调用具有错误的声明,则将引发TypeError
。对于替换类的mock,它们的返回值(“实例”)将具有与类相同的规格。请参阅create_autospec()
函数和Autospeccing。除了
autospec=True
,您可以传递autospec=some_object
,以使用任意对象作为规范,而不是要替换的对象。默认情况下,
patch()
将无法替换不存在的属性。如果传入create=True
,并且该属性不存在,patch将在调用修补函数时为您创建属性,然后再次删除它。这对于针对您的生产代码在运行时创建的属性编写测试很有用。它默认关闭,因为它可能是危险的。随着它的开启,你可以编写针对实际不存在的API的通过测试!注意
在版本3.5中更改:如果您在模块中修补内置函数,那么您不需要传递
create=True
,它将默认添加。Patch可以用作
TestCase
类装饰器。它通过装饰类中的每个测试方法来工作。当测试方法共享共同的修补集时,这减少了样板代码。patch()
通过查找以patch.TEST_PREFIX
开头的方法名称来查找测试。默认情况下,这是'test'
,它匹配unittest
查找测试的方式。您可以通过设置patch.TEST_PREFIX
来指定备用前缀。补丁可以用作上下文管理器,带有with语句。这里修补适用于with语句后的缩进块。如果使用“as”,那么修补的对象将绑定到“as”之后的名称;非常有用,如果
patch()
正在为你创建一个模拟对象。patch()
使用任意关键字参数。这些将在构建时传递到Mock
(或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()
替换类 mock会有相同的规格。
>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()
如果您想对默认的MagicMock
使用替代类来创建模拟,则new_callable参数很有用。例如,如果您想要使用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()
正在为你创建一个模拟器时,通常你需要做的第一件事是配置模拟。一些配置可以在调用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
26.5.3.2. patch.object ¶
-
patch.
object
(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ 使用模拟对象在对象(目标)上修补指定成员(属性)。
patch.object()
可用作装饰器,类装饰器或上下文管理器。参数新,规格,创建,spec_set,autospec和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,create和patch.object()
的其他参数的含义与patch()
26.5.3.3. patch.dict ¶
-
patch.
dict
(in_dict, values=(), clear=False, **kwargs)¶ 修补字典或像对象的字典,并在测试后将字典恢复到其原始状态。
in_dict可以是字典或像容器的映射。如果它是一个映射,那么它必须至少支持获取,设置和删除项目以及迭代键。
in_dict也可以是指定字典名称的字符串,然后通过导入获取字典。
值可以是在字典中设置的值的字典。值也可以是
(键, 值)
对的迭代。如果清除为真,则在设置新值之前,字典将被清除。
patch.dict()
也可以使用任意关键字参数调用,以设置字典中的值。patch.dict()
可用作上下文管理器,装饰器或类装饰器。当用作类装饰器patch.dict()
荣誉patch.TEST_PREFIX
用于选择要包装的方法。
patch.dict()
可用于向字典添加成员,或者只是让测试更改字典,并确保在测试结束时恢复字典。
>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert 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()
可以用于像字典那样不是字典的对象。至少他们必须支持项目获取,设置,删除和迭代或成员资格测试。This corresponds to the magic methods __getitem__()
, __setitem__()
, __delitem__()
and either __iter__()
or __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']
26.5.3.4. patch.multiple ¶
-
patch.
multiple
(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶ 在单个调用中执行多个修补程序。它需要修补对象(作为对象或通过导入获取对象的字符串)和关键字参数:
with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): ...
如果您希望
patch.multiple()
为您创建模型,请使用DEFAULT
作为值。在这种情况下,创建的mock通过关键字传递到修饰函数中,当patch.multiple()
用作上下文管理器时,会返回字典。patch.multiple()
可以用作装饰器,类装饰器或上下文管理器。参数spec,spec_set,create,autospec和new_callable对于patch()
。这些参数将应用于由patch.multiple()
完成的所有补丁。当用作类装饰器
patch.multiple()
荣誉patch.TEST_PREFIX
用于选择要包装的方法。
如果您希望patch.multiple()
为您创建mock,则可以使用DEFAULT
作为值。如果使用patch.multiple()
作为装饰器,那么创建的mock将通过关键字传递到修饰函数。
>>> thing = object()
>>> other = object()
>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
... assert isinstance(thing, MagicMock)
... assert isinstance(other, MagicMock)
...
>>> test_function()
patch.multiple()
can be nested with other patch
decorators, but put arguments passed by keyword after any of the standard arguments created by 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
...
26.5.3.5.补丁方法:启动和停止¶
所有修补程序都有start()
和stop()
方法。这些使得在setUp
方法中进行修补更简单,或者您希望在没有嵌套装饰器或语句的情况下执行多个修补程序。
To use them call patch()
, patch.object()
or patch.dict()
as normal and keep a reference to the returned patcher
object. 然后,您可以调用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
这种情况的典型用例可能是在TestCase
的setUp
方法中执行多个修补程序:
>>> class MyTest(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(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
开头的补丁。
26.5.3.6.补丁内置¶
您可以修补模块中的任何内置板。以下示例修补程序内置ord()
:
>>> @patch('__main__.ord')
... def test(mock_ord):
... mock_ord.return_value = 101
... print(ord('c'))
...
>>> test()
101
26.5.3.7. 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
26.5.3.8. Nesting Patch Decorators¶
如果你想要执行多个补丁,那么你可以简单地叠加装饰器。
您可以使用此模式堆叠多个修补程序装饰:
>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
... assert SomeClass.static_method is mock1
... assert SomeClass.class_method is mock2
... SomeClass.static_method('foo')
... SomeClass.class_method('bar')
... return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')
注意,装饰器从底部向上应用。这是Python应用装饰器的标准方式。传递到测试函数中的创建的mock的顺序与此顺序相匹配。
26.5.3.9. Where to patch¶
patch()
通过(暂时)将名称指向的对象更改为另一个对象。可以有许多名称指向任何单独的对象,因此为了修补工作,您必须确保修补被测试系统使用的名称。
基本原理是,您在对象查找的位置打补丁,这不一定与定义的位置相同。一些例子将有助于澄清这一点。
想象一下,我们有一个项目,我们想用下面的结构进行测试:
a.py
-> Defines SomeClass
b.py
-> from a import SomeClass
-> some_function instantiates SomeClass
现在我们要测试some_function
,但我们要使用patch()
来模拟SomeClass
。问题是,当我们导入模块b,我们将要做,然后从模块a导入SomeClass
。如果我们使用patch()
来模拟a.SomeClass
,那么它对我们的测试没有影响;模块b已经引用了real SomeClass
,它看起来像我们的修补没有效果。
关键是要修补SomeClass
它在哪里使用(或在哪里查找)。In this case some_function
will actually look up SomeClass
in module b, where we have imported it. 修补应该看起来像:
@patch('b.SomeClass')
但是,考虑替代从 a import SomeClass
模块b 导入 a
和some_function
使用a.SomeClass
。这两种导入形式是常见的。在这种情况下,我们要修补的类正在模块中查找,因此我们必须修补a.SomeClass
:
@patch('a.SomeClass')
26.5.3.10. Patching Descriptors and Proxy Objects¶
patch和patch.object正确地修补和恢复描述器:类方法,静态方法和属性。您应该将这些修补在类而不是实例。他们还使用代理属性访问的一些对象,如django设置对象。
26.5.4. MagicMock and magic method support¶
26.5.4.1. Mocking Magic Methods¶
Mock
支持模拟Python协议方法,也称为“魔法方法”。这允许模拟对象替换实现Python协议的容器或其他对象。
因为魔法方法不同于正常方法[2],这种支持已经被专门实现。这意味着只支持特定的魔法方法。支持的列表包括几乎。如果有任何需要的缺失,请让我们知道。
你通过将你感兴趣的方法设置为函数或模拟实例来模拟魔法方法。如果使用函数,则必须将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关键字参数创建模拟,则尝试设置不在规范中的魔法将引发AttributeError
。
支持的魔法方法的完整列表是:
__hash__
,__sizeof__
,__repr__
和__str__
__dir__
,__format__
和__subclasses__
__floor__
,__trunc__
和__ceil__
- Comparisons:
__lt__
,__gt__
,__le__
,__ge__
,__eq__
and__ne__
- 容器方法:
__getitem__
,__setitem__
,__delitem__
,__contains__
,__len__
,__iter__
,__reversed__
和__missing__
- 上下文管理器:
__enter__
和__exit__
- 一元数字方法:
__neg__
,__pos__
和__invert__
- The numeric methods (including right hand and in-place variants):
__add__
,__sub__
,__mul__
,__matmul__
,__div__
,__truediv__
,__floordiv__
,__mod__
,__divmod__
,__lshift__
,__rshift__
,__and__
,__xor__
,__or__
, and__pow__
- 数字转换方法:
__complex__
,__int__
,__float__
和__index__
- 描述器方法:
__get__
,__set__
和__delete__
- Pickling:
__reduce__
,__reduce_ex__
,__getinitargs__
,__getnewargs__
,__getstate__
and__setstate__
存在以下方法,但是不是受支持,因为它们正在被mock使用,无法动态设置,或可能导致问题:
__getattr__
,__setattr__
,__init__
和__new__
__prepare__
,__instancecheck__
,__subclasscheck__
,__del__
26.5.4.2. Magic Mock¶
有两种MagicMock
变体:MagicMock
和NonCallableMagicMock
。
- class
unittest.mock.
MagicMock
(*args, **kw)¶ MagicMock
是Mock
的子类,具有大多数魔法方法的默认实现。您可以使用MagicMock
,而无需自己配置魔术方法。构造函数参数具有与
Mock
相同的含义。如果您使用spec或spec_set参数,那么将会创建只有规范中存在的魔术方法。
- class
unittest.mock.
NonCallableMagicMock
(*args, **kw)¶ MagicMock
的不可调用版本。除了return_value和side_effect,构造函数参数与
MagicMock
具有相同的含义,它们对不可调用的模拟没有意义。
魔法方法使用MagicMock
对象进行设置,因此您可以配置它们并以通常的方式使用它们:
>>> mock = MagicMock()
>>> mock[3] = 'fish'
>>> mock.__setitem__.assert_called_with(3, 'fish')
>>> mock.__getitem__.return_value = 'result'
>>> mock[2]
'result'
默认情况下,需要许多协议方法来返回特定类型的对象。这些方法预先配置了默认返回值,因此如果您对返回值不感兴趣,可以使用这些方法,而不必执行任何操作。如果要更改默认值,您仍然可以手动设置返回值。
方法及其默认值:
__lt__
:未实现__gt__
:未实现__le__
:未实施__ge__
:未实现__int__
:1__contains__
:False__len__
:0__iter__
:iter([])__exit__
:False__complex__
:1j__float__
:1.0__bool__
:True__index__
:1__hash__
:模拟的默认哈希__str__
:默认str为模拟__sizeof__
:默认sizeof为模拟
例如:
>>> mock = MagicMock()
>>> int(mock)
1
>>> len(mock)
0
>>> list(mock)
[]
>>> object() in mock
False
两个等式方法,__eq__()
和__ne__()
是特殊的。它们使用side_effect
属性对identity进行默认等式比较,除非您更改其返回值以返回其他值:
>>> MagicMock() == 3
False
>>> MagicMock() != 3
True
>>> mock = MagicMock()
>>> mock.__eq__.return_value = True
>>> mock == 3
True
MagicMock.__iter__()
的返回值可以是任何可迭代对象,并且不需要是迭代器:
>>> mock = MagicMock()
>>> mock.__iter__.return_value = ['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
如果返回值是一个迭代器,那么迭代一次就会消耗它,随后的迭代将产生一个空列表:
>>> mock.__iter__.return_value = iter(['a', 'b', 'c'])
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
[]
MagicMock
具有所有支持的魔法方法,除了一些晦涩的和过时的。你仍然可以设置这些,如果你想要的。
默认情况下,在MagicMock
中支持但未设置的魔法方法是:
__subclasses__
__dir__
__format__
__get__
,__set__
和__delete__
__reversed__
和__missing__
__reduce__
,__reduce_ex__
,__getinitargs__
,__getnewargs__
,__getstate__
和__setstate__
__getformat__
和__setformat__
[2] | 魔法方法应该在类上而不是实例。不同版本的Python对应用此规则不一致。支持的协议方法应该与所有受支持的Python版本配合使用。 |
[3] | 该函数基本上是连接到类,但每个Mock 实例保持与其他实例隔离。 |
26.5.5. Helpers¶
26.5.5.1. sentinel ¶
-
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
26.5.5.2. DEFAULT¶
-
unittest.mock.
DEFAULT
¶ DEFAULT
对象是预创建的哨兵(实际上是sentinel.DEFAULT
)。它可以由side_effect
函数使用,以指示应使用正常返回值。
26.5.5.3.调用¶
-
unittest.mock.
call
(*args, **kwargs)¶ call()
is a helper object for making simpler assertions, for comparing withcall_args
,call_args_list
,mock_calls
andmethod_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
call
对象是(位置args,关键字args)或(名称,位置args,关键字args)的元组,取决于它是如何构造的。When you construct them yourself this isn’t particularly interesting, but the call
objects that are in the Mock.call_args
, Mock.call_args_list
and Mock.mock_calls
attributes can be introspected to get at the individual arguments they contain.
The call
objects in Mock.call_args
and Mock.call_args_list
are two-tuples of (positional args, keyword args) whereas the call
objects in Mock.mock_calls
, along with ones you construct yourself, are three-tuples of (name, positional args, keyword args).
你可以使用他们的“tupleness”拉出单独的参数以进行更复杂的自省和断言。位置参数是一个元组(如果没有位置参数,则为空元组),并且关键字参数是字典:
>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> args, kwargs = kall
>>> args
(1, 2, 3)
>>> kwargs
{'arg2': 'two', 'arg': 'one'}
>>> args is kall[0]
True
>>> 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
{'arg2': 'three', 'arg': 'two'}
>>> name is m.mock_calls[0][0]
True
26.5.5.4. create_autospec ¶
-
unittest.mock.
create_autospec
(spec, spec_set=False, instance=False, **kwargs)¶ 使用另一个对象作为spec来创建一个mock对象。模拟上的属性将使用spec对象上的相应属性作为其规范。
被嘲笑的函数或方法将检查它们的参数,以确保它们用正确的声明调用。
如果spec_set是
True
,那么尝试设置spec对象上不存在的属性将引发AttributeError
。如果一个类被用作spec,那么mock的返回值(类的实例)将具有相同的规格。您可以通过传递
instance=True
来使用类作为实例对象的规范。返回的模拟只有当模拟的实例可调用时才可调用。create_autospec()
也采用传递给创建的mock的构造函数的任意关键字参数。
See Autospeccing for examples of how to use auto-speccing with create_autospec()
and the autospec argument to patch()
.
26.5.5.5. ANY¶
-
unittest.mock.
ANY
¶
有时您可能需要在调用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
26.5.5.6. FILTER_DIR¶
-
unittest.mock.
FILTER_DIR
¶
FILTER_DIR
是一个模块级变量,控制模拟对象响应dir()
的方式(仅适用于Python 2.6或更高版本)。默认值为True
,它使用下面介绍的过滤器,仅显示有用的成员。如果您不喜欢此过滤,或需要将其关闭以进行诊断,请设置mock.FILTER_DIR = False t0 >。
通过过滤,dir(some_mock)
只显示有用的属性,并包括通常不会显示的任何动态创建的属性。如果模拟是使用spec(或当然是autospec)创建的,那么将显示原始的所有属性,即使它们尚未被访问:
>>> dir(Mock())
['assert_any_call',
'assert_called_once_with',
'assert_called_with',
'assert_has_calls',
'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
。
26.5.5.7. mock_open ¶
-
unittest.mock.
mock_open
(mock=None, read_data=None)¶ A helper function to create a mock to replace the use of
open()
. It works foropen()
called directly or used as a context manager.The mock argument is the mock object to configure. If
None
(the default) then aMagicMock
will be created for you, with the API limited to methods or attributes available on standard file handles.read_data is a string for the
read()
,readline()
, andreadlines()
methods of the file handle to return. Calls to those methods will take data from read_data until it is depleted. The mock of these methods is pretty simplistic: every time the mock is called, the read_data is rewound to the start. If you need more control over the data that you are feeding to the tested code you will need to customize this mock for yourself. When that is insufficient, one of the in-memory filesystem packages on PyPI can offer a realistic filesystem for testing.在版本3.4中已更改:添加了
readline()
和readlines()
支持。read()
的模拟更改为使用read_data,而不是在每次调用时返回它。在3.5版本中更改: read_data现在在每次调用mock时重置。
使用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'
26.5.5.8. Autospeccing¶
自动指定基于模拟的现有spec
功能。它将mock的api限制为原始对象(spec)的api,但它是递归的(延迟实现),使得mocks的属性只有与spec的属性相同的api。此外,模拟函数/方法具有与原始函数相同的调用声明,因此如果调用不正确,它们引发TypeError
。
在我解释自动投标如何工作之前,这里是为什么它需要。
Mock
是一个非常强大和灵活的对象,但是当它用于从被测系统中模拟出对象时,会遇到两个缺陷。这些缺陷之一特定于Mock
api,另一个是使用模拟对象的更一般的问题。
首先是Mock
特有的问题。Mock
有两个非常方便的assert方法: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而不是真实对象的代码的任何测试仍将通过。这意味着即使你的代码被破坏,你的测试也可以通过。
注意,这是为什么你需要集成测试和单元测试的另一个原因。孤立地测试一切都是好的,如果你不测试你的单元是如何“连接在一起”,仍然有很多空间的测试可能已经捕获的错误。
mock
已经提供了一个功能来帮助这个,称为投标。如果你使用类或实例作为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()
如果你使用patch()
的autospec=True
参数,那么被替换的对象将被用作spec对象。因为指定是“lazily”(规范是作为模型上的属性被访问创建的),可以使用它与非常复杂或深层嵌套的对象(如导入模块导入模块的模块),而没有大的性能命中。
下面是一个使用示例:
>>> 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)
规范也适用于实例化的类(即。嘲笑的返回值):
>>> 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更改所导致的错误。
除了使用autospec到patch()
,还有一个create_autospec()
可直接创建自动指定的mock:
>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>
这是不是没有注意和限制,但这是为什么它不是默认行为。为了知道什么属性可用在spec对象,autospec必须introspect(访问属性)规范。当你遍历模型上的属性时,原始对象的相应遍历在引擎盖下发生。如果你的任何specered对象有属性或描述器可以触发代码执行,那么你可能无法使用autospec。另一方面,它更好地设计你的对象,使内省安全[4]。
更严重的问题是,在__init__()
方法中创建实例属性是很常见的,并且根本不存在于类上。autospec无法知道任何动态创建的属性,并将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'
有几种不同的方法来解决这个问题。最简单,但不一定是最恼人的方法是在创建之后在模拟上简单地设置所需的属性。由于autospec不允许抓取规范中不存在的属性,因此不会阻止您设置它们:
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a = 33
...
有一个更积极的版本的spec和autospec,阻止您设置不存在的属性。如果您希望确保代码只有设置有效属性,这很有用,但显然它会阻止这种特殊情况:
>>> 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
将作为规范是无用的,因为它不会让您访问任何属性或方法。由于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()
支持这个 - 你可以简单地传递替代对象作为autospec参数:
>>> 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() 的调用已完成。 |