– 模拟对象库 – 开发工具(Python教程)(参考资料)
unittest.mock
– 模拟对象库
3.3版本中的新功能.
源代码: Lib / unittest / mock.py
unittest.mock
是一个用于在Python中进行测试的库。它允许你用模拟对象替换被测系统的部分,并断言它们是如何被使用的.
unittest.mock
提供了一个核心Mock
class删除需要在整个测试套件中创建大量存根。执行动作后,您可以断言使用哪些方法/属性以及调用它们的参数。您也可以正常方式指定返回值和设置所需属性.
另外,mock提供了一个patch()
装饰器来处理测试范围内的patchingmodule和类级别属性,以及sentinel
用于创建唯一对象。有关如何使用Mock
, MagicMock
和patch()
.
的一些示例,请参阅快速指南。模拟非常易于使用,设计用于unittest
。Mockis基于’动作 – >断言’模式而不是’记录 – >重放’被许多模拟框架使用
对于早期版本的Python,有一个unittest.mock
的后端,在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()
装饰器/上下文管理器使得在被测模块中模拟类或对象变得容易。您指定的对象将在测试期间替换为amock(或其他对象),并在测试结束时恢复:
>>> 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()
你修改它们所在的命名空间中的对象很重要起来。这通常是直截了当的,但是为了快速引导在哪里修补.
除了装饰器patch()
可以在withstatement中用作上下文管理器:
>>> with patch.object(ProductionClass, "method", return_value=None) as mock_method:... thing = ProductionClass()... thing.method(1, 2, 3)...>>> mock_method.assert_called_once_with(1, 2, 3)
还有patch.dict()
用于在范围内设置值,并且在测试时将字典恢复到原始状态:
>>> foo = {"key": "value"}>>> original = foo.copy()>>> with patch.dict(foo, {"newkey": "newvalue"}, clear=True):... assert foo == {"newkey": "newvalue"}...>>> assert foo == original
Mock支持Python的模拟魔法。使用魔法的最简单方法是使用MagicMock
类。允许您执行以下操作:
>>> mock = MagicMock()>>> mock.__str__.return_value = "foobarbaz">>> str(mock)"foobarbaz">>> mock.__str__.assert_called_with()
Mock允许您将函数(或其他Mock实例)分配给魔术方法,并且它们将被适当地调用。MagicMock
class只是一个Mockvariant,它拥有为你预先创建的所有魔术方法(好吧,无论如何都是有用的).
以下是使用普通Mockclass的魔术方法的示例:
>>> mock = Mock()>>> mock.__str__ = Mock(return_value="wheeeeee")>>> str(mock)"wheeeeee"
为了确保测试中的模拟对象与它们替换的对象具有相同的api,可以使用自动指定。自动指定可以通过autospec参数补丁来完成,或者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
是一个灵活的模拟对象,旨在替代整个代码中存根和测试双打的使用。模拟是可调用的,并在访问它们时创建属性作为新模拟[1]。访问相同的属性将始终返回相同的模拟。模拟记录了你如何使用它们,让你可以断言你的代码对它们做了什么.
MagicMock
是Mock
的子类,所有魔术方法都已预先创建并可以使用。还有不可调用的变体,当你模拟不可调用的对象时很有用:NonCallableMock
和NonCallableMagicMock
patch()
装饰器可以很容易地用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
有几个可选参数,用于指定Mock对象的行为:-
spec:这可以是字符串列表,也可以是充当模拟规范的现有对象(aclass或instance)宾语。如果你传入一个对象,那么通过在对象上调用dir来形成字符串列表(不包括不支持的魔术属性和方法)。访问不在此列表中的任何属性将引发
AttributeError
.如果spec是一个对象(而不是字符串列表)然后
__class__
返回spec对象的类。这允许嘲笑传递isinstance()
测试 -
spec_set:spec的更严格的变体。如果使用,尝试set或获取不在作为spec_set传递的对象上的模拟的属性将引发
AttributeError
. -
side_effect:每当调用时调用的函数模拟被称为。见
side_effect
属性。用于提高异常或动态更改返回值。使用与mock相同的参数调用该函数,除非它返回DEFAULT
,此函数的返回值用作返回值.或者side_effect可以是异常类或实例。在这种情况下,当调用mock时会引发异常.
如果side_effect是一个可迭代的,那么每次调用mock都会从iterable返回下一个值
Aside_effect可以通过将其设置为
None
. -
return_value来清除:调用模拟时返回的值。默认情况下,这是一个新的Mock(在第一次访问时创建)。请参阅
return_value
属性 -
unsafe:默认情况下,如果任何属性以assert或assret开头,则会引发
AttributeError
。传递unsafe=True
将允许访问这些属性.新版本3.5.
-
wraps:用于包装模拟对象的项目。如果wraps不是
None
那么调用Mock会将调用传递给包装对象(返回实际结果)。模拟器上的属性访问将返回包装wrappedobject的相应属性的aMock对象(因此,尝试访问不存在的属性会导致AttributeError
)。如果模拟有明确的return_value set然后调用没有传递给包装对象而是return_value被返回
-
name:如果mock有一个名字,那么它将用于weock的repr。这对调试很有用。名称传播到childmocks.
也可以使用任意关键字参数调用模拟。这些将用于在模拟创建后在模拟上设置属性。详见
configure_mock()
方法.assert_called
(*args, **kwargs )-
断言模拟被称为至少一次.
>>> mock = Mock()>>> mock.method()<Mock name="mock.method()" id="...">>>> mock.method.assert_called()
新版本3.6.
assert_called_once
(*args, **kwargs)-
声称模拟被称为一次。
>>> 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)-
断言使用指定的参数调用了mock .
断言如果模拟有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。
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.calledTrue>>> mock.reset_mock()>>> mock.calledFalse
在版本3.6中更改:在reset_mock函数中添加了两个仅关键字参数.
这对您有用想要制作一系列使用相同对象的断言。请注意
reset_mock()
doesn’t默认使用正常分配来清除值,side_effect
或任何子属性。如果你想重置return_value或side_effect
,然后传递相应的参数为True
。儿童模拟和返回值模拟(如果有的话)也被重置.
mock_add_spec
(spec, spec_set=False)-
为模拟添加规范。spec可以是字符串的对象或列表。只有spec上的属性可以从mock中获取asattributes
如果spec_set为真,则只能设置规范上的属性.
attach_mock
(mock, attribute)-
附加模拟作为此属性的属性,替换其名称和父母。对附件模拟的调用将记录在这个的
method_calls
和mock_calls
属性中.
configure_mock
(**kwargs)-
通过关键字参数在mock上设置属性.
可以使用标准点表示法在childmocks上设置属性加返回值和副作用,并在方法调用中解压缩字典:
>>> 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
_get_child_mock
(**kw)-
为属性和返回值创建子模拟。默认的子模拟将与父类型相同.Mock的子类可能想要覆盖它以自定义方式模拟的方式.
对于不可调用的模拟,将使用可调用的变体(而不是任何自定义的子类).
called
-
表示是否已调用模拟对象的布尔值:
>>> mock = Mock(return_value=None)>>> mock.calledFalse>>> mock()>>> mock.calledTrue
call_count
-
一个整数,告诉你模拟对象被调用了多少次:
>>> mock = Mock(return_value=None)>>> mock.call_count0>>> mock()>>> mock()>>> mock.call_count2
return_value
-
设置它来配置通过调用mock:
>>> 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_value3>>> mock()3
side_effect
-
这可以是调用mock时要调用的函数,要引发的可迭代或异常(类或实例).
如果你传入一个函数,它将被调用与weock相同的参数,除非函数返回
DEFAULT
然后单独的thecall到mock将返回函数返回的任何内容。如果函数返回DEFAULT
则模拟将返回其正常值(来自return_value
).如果传入一个iterable,它将用于检索迭代器,该迭代器必须在每次调用时产生一个值。此值可以是要引发的异常实例,也可以是从调用中返回的值(
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
>>> 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被调用的任何有序参数(或一个空元组),第二个成员是anykeyword参数(或一个空字典).>>> mock = Mock(return_value=None)>>> print(mock.call_args)None>>> mock()>>> mock.call_argscall()>>> mock.call_args == ()True>>> mock(3, 4)>>> mock.call_argscall(3, 4)>>> mock.call_args == ((3, 4),)True>>> mock(3, 4, 5, key="fish", next="w00t!")>>> mock.call_argscall(3, 4, 5, key="fish", next="w00t!")
call_args
,以及列表的成员call_args_list
,method_calls
和mock_calls
是call
对象。这些是元组,因此可以解压缩它们以获得单个参数并进行更复杂的断言。看到称为元组.
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 == expectedTrue
进行比较
call_args_list
的成员是call
对象。这些可以作为元组包装以获得各个参数。看到作为元组调用.
method_calls
-
除了跟踪对自己的调用之外,模拟还跟踪对方法和属性的调用,以及their方法和属性:
>>> 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
对象。这些可以作为元组包装以获得各个参数。看调用作为元组.
mock_calls
-
mock_calls
记录all调用模拟对象,它的方法,魔法and返回值mocks.>>> 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 == expectedTrue
mock_calls
的成员是call
对象。这些可以作为元组包装以获得各个参数。看作为元组调用.注意
的方式
mock_calls
记录意味着在进行嵌套调用的情况下,祖先调用的参数不会被记录,因此总是比较相等:>>> mock = MagicMock()>>> mock.top(a=3).bottom()<MagicMock name="mock.top().bottom()" id="...">>>> mock.mock_calls[call.top(a=3), call.top().bottom()]>>> mock.mock_calls[-1] == call.top(a=-1).bottom()True
-
- class
unittest.mock.
NonCallableMock
(spec=None, wraps=None, name=None, spec_set=None, **kwargs) -
一个不可调用的版本
Mock
。构造函数参数与Mock
的含义相同,但return_value和side_effect除外,它们对不可调用的模拟没有任何意义.
使用类或实例作为spec
或spec_set
的模拟对象能够通过isinstance()
tests:
>>> mock = Mock(spec=SomeClass)>>> isinstance(mock, SomeClass)True>>> mock = Mock(spec_set=SomeClass())>>> isinstance(mock, SomeClass)True
Mock
类支持嘲弄魔法。见 magicmethods 详细信息.
模拟类和patch()
装饰器都采用任意的关键字参数进行配置。为了 patch()
装饰器关键字被传递给正在创建的模拟的构造函数。关键字arguments用于配置mock的属性:
>>> m = MagicMock(attribute=3, other="fish")>>> m.attribute3>>> 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 )当匹配调用mock时,将反转规范对象的签名。因此,它可以匹配实际调用的参数,无论它们是通过位置传递还是通过名称传递:
>>> def f(a, b, c): pass...>>> mock = Mock(spec=f)>>> mock(1, 2, c=3)<Mock name="mock()" id="140161580456576">>>> mock.assert_called_with(1, 2, 3)>>> mock.assert_called_with(a=1, b=2, c=3)
这适用于assert_called_with()
,assert_called_once_with()
, assert_has_calls()
和assert_any_call()
。当自动指定时,它也会应用于模拟对象的方法调用.
更改版本3.4:在已推出和自动指定的模拟对象上添加了签名内省.
- class
unittest.mock.
PropertyMock
(*args, **kwargs) -
一个模拟用作类的属性或其他描述符.
PropertyMock
提供__get__()
和__set__()
你可以在获取时指定一个返回值.从一个对象中获取一个
PropertyMock
实例调用mock,不带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
附加到模拟对象。相反,你可以将它附加到模拟类型对象:
>>> m = MagicMock()>>> p = PropertyMock(return_value=3)>>> type(m).foo = p>>> m.foo3>>> p.assert_called_once_with()
调用
模拟对象是可调用的。该调用将返回设置为的值return_value
属性。默认返回值是一个新的Mockobject;它是在第一次访问返回值时创建的(eitherexuallicit或通过调用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
是一个函数,然后该函数返回的是对模拟返回的调用。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仍然返回默认的返回值(一个新的模拟),orany设置返回值,那么有两种方法可以做到这一点。从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
也可以是任何可迭代的对象。重复调用mockwill会从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
回到hasattr()
来电,或者举起一个AttributeError
何时获取属性。你可以通过提供一个对象作为一个模拟的spec
来做到这一点,但这并不总是方便的.
你通过删除它来“阻止”属性。删除后,访问一个属性会引发一个AttributeError
.
>>> mock = MagicMock()>>> hasattr(mock, "m")True>>> del mock.m>>> hasattr(mock, "m")False>>> del mock.f>>> mock.fTraceback (most recent call last): ...AttributeError: f
模拟名称和名称属性
因为“name”是Mock
构造函数,如果您希望yourmock对象具有“name”属性,则不能在创建时将其传递给它。有两种选择。一个选项是使用configure_mock()
:
>>> mock = MagicMock()>>> mock.configure_mock(name="my_name")>>> mock.name"my_name"
一个更简单的选项是在模拟创建后简单地设置“name”属性:
>>> mock = MagicMock()>>> mock.name = "foo"
将Mocks作为属性附加
当您附加模拟作为另一个模拟的属性(或作为返回值)时,它将成为该模拟的“子”。对孩子的呼叫记录在父母的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] | 唯一的例外是魔术方法和属性(具有前导和尾随双下划线的那些)。模拟并没有创造这些但是提升了AttributeError 。这是因为解释器会经常隐含地请求这些方法,并获得very混淆得到一个新的Mock对象,当它需要一个神奇的方法。如果需要魔法支持请看魔术方法. |
小贴士
修补程序装饰器仅用于在它们装饰的函数范围内修补对象。即使引发异常,它们也会自动为您执行取消修补。所有这些函数也可以用于语句或类装饰器.
补丁
注意
patch()
使用起来很简单。关键是在最佳命名空间中进行修补。见补丁的地方.
unittest.mock.
patch
(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs )-
patch()
充当函数装饰器,类装饰器或上下文管理器。在函数体内或使用语句,target用new宾语。当函数/ with语句退出补丁撤消时如果new被省略,则目标被替换为
MagicMock
。如果patch()
用作装饰器并且new isomitted,则创建的模拟作为额外参数传递给装饰函数。如果patch()
用作上下文管理器,则contextmock由上下文管理器返回.target应该是
"package.module.ClassName"
形式的字符串。导入target并将指定的对象替换为new对象,因此target必须可以从您正在调用的环境中导入patch()
。执行修饰函数时导入目标,而不是在装饰时导入// spec和spec_set关键字参数传递给
MagicMock
如果补丁是为你创建一个.另外你可以传递
spec=True
或spec_set=True
,这会导致补丁传入被模拟的对象作为spec / spec_set对象.new_callable允许您指定一个不同的类或可调用对象,它们将被调用以创建new宾语。默认情况下
MagicMock
isused.spec更强大的形式是autospec。如果设置
autospec=True
,则将使用来自被替换对象的规范创建模拟。模拟的所有属性也将具有要替换的对象的对应属性的规范。被模拟的方法和函数将检查它们的参数,并且如果它们被称为错误签名则会引发TypeError
。对于mocksreplacing类,它们的返回值(’instance’)将具有与类相同的规范。看create_autospec()
功能和自动指定.而不是
autospec=True
你可以通过autospec=some_object
使用任意对象作为规范而不是一个被替换默认
patch()
将无法替换不存在的属性。如果你传入create=True
,并且该属性不存在,patch将在调用修补函数时为您创建属性,并在修补函数退出后再次删除。这对于对生产代码在运行时创建的属性进行书写测试很有用。它默认为offoff,因为它可能很危险。打开它后,你可以对实际不存在的API进行传递测试!注意
在版本3.5中更改:如果你在模块中修补内置函数那么你不需要传递
create=True
,它会被默认添加.补丁可以用作
TestCase
类装饰器。它的工作原理是在课堂上装饰每个测试方法。当您的测试方法共享一个共同的补丁集时,这会减少boilerplatecode。patch()
通过查找以patch.TEST_PREFIX
开头的方法名来查找测试。默认这是"test"
,它匹配unittest
找到测试的方式。你可以指定一个通过设置patch.TEST_PREFIX
.Patch可以用作上下文管理器,带有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
instance。如果在被测试的代码中实例化了类,则它将是return_value
将要使用的模拟器
如果类被多次实例化,你可以使用side_effect
每次返回一个新的模拟。或者你可以把return_value设置成你想要的任何东西.
在补丁课上配置instances方法的返回值你必须在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()
正在替换class,然后创建的模拟器的值将具有相同的规格
>>> 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()
正在为你创建一个模拟,你需要做的第一件事就是配置mock。其中一些配置可以在调用补丁时完成。您传入调用的任意关键字都将用于在创建的mock上设置属性:
>>> patcher = patch("__main__.thing", first="one", second="two")>>> mock_thing = patcher.start()>>> mock_thing.first"one">>> mock_thing.second"two"
以及创建的模拟属性的属性,如return_value
和side_effect
,儿童嘲笑运动也可以配置。这些在语法上没有直接传递askeyword参数有效,但是使用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
in调用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)-
修补指定成员(attribute)一个带有mockobject的对象(target)
patch.object()
可以用作装饰器,类装饰器或上下文管理器。参数new, spec, create, spec_set, autospec和new_callable的含义与patch()
的含义相同。就像patch()
,patch.object()
使用任意关键字参数来配置它创建的模拟对象.当用作类装饰器时
patch.object()
尊重patch.TEST_PREFIX
选择要包装的方法。
您可以使用三个参数或两个参数调用patch.object()
。三个参数形式将要修补的对象,属性名称和对象替换属性.
当用两个参数形式调用时,你省略了替换对象,并为你创建了amock并作为额外的参数传递给decoratedfunction:
>>> @patch.object(SomeClass, "class_method")... def test(mock_method):... SomeClass.class_method(3)... mock_method.assert_called_with(3)...>>> test()
patch.dict
patch.
dict
(in_dict, values=(), clear=False, **kwargs)-
填写字典,或字体之类的对象,并将字典恢复到其原始状态之后test.
in_dict可以是字典或像容器一样的映射。如果它是惊吓那么它必须至少支持获取,设置和删除itemsplus迭代键.
in_dict也可以是一个字符串,指定字典的名称,然后通过导入它取出.
values可以是要在字典中设置的值的字典。values也可以是
(key, value)
对的可迭代如果clear为真,那么字典将在新值设置之前被清除.
patch.dict()
也可以用字典中的setvalues的任意关键字参数调用.patch.dict()
可以用作上下文管理器,装饰器或类装饰器。当用作类装饰器时patch.dict()
尊重patch.TEST_PREFIX
选择哪种方法包装
patch.dict()
可以用来将成员添加到字典中,或者只是让testchange一个字典,并确保在testends时可以恢复字典.
>>> 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
Keywords可以在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()
为你创造机会。在这种情况下,创建的模拟按关键字传递给Decorativefunction,当patch.multiple()
用作上下文管理器时返回字典.patch.multiple()
可以用作装饰器,类装饰器或上下文管理器。参数spec, spec_set, create, autospec和new_callable的含义与patch()
的含义相同。这些参数将应用于all由patch.multiple()
.完成的补丁当用作类装饰器时
patch.multiple()
尊重patch.TEST_PREFIX
选择要包装的方法
如果你想patch.multiple()
为你创建模拟,那么你可以使用DEFAULT
作为值。如果使用patch.multiple()
作为decoratorthen,创建的模拟通过关键字传递到装饰函数.
>>> 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
装饰器嵌套,但是通过关键字将参数传递给after由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()
用作上下文管理器,则文本管理器返回的值是一个创建模拟的字典按名称键入:
>>> 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()
asnormal并保留对返回的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
TestCase
>>> 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
As一个额外的奖励你不再需要保持对
patch.
stopall
也可以使用(//停止所有已启动的补丁////)-
停止所有活动的补丁。仅停止使用
start
.
patch builtins
启动的补丁。您可以修补模块中的任何内置函数。以下示例patchbuiltin 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>>> value3
嵌套补丁装饰器来告知不同前缀的补丁
如果你想要执行多个补丁,那么你可以简单地堆叠装配器.
你可以使用这个模式堆叠多个补丁装饰器:
>>> @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应用装饰器的标准方式。创建的mockspassed到您的测试函数的顺序与此顺序匹配.
在哪里修补
patch()
工作(暂时)改变一个name指向另一个。可以有许多名称指向任何单个对象,软件修补工作必须确保您修补被测系统使用的名称.
基本原则是你修补一个对象looked up的位置,它不一定与定义它的位置相同。几个例子将有助于澄清这一点.
想象一下,我们有一个我们想用以下结构测试的项目:
a.py -> Defines SomeClassb.py -> from a import SomeClass -> some_function instantiates SomeClass
现在我们要测试some_function
但我们想嘲笑SomeClass
使用patch()
。问题是,当我们导入模块b时,我们将使用todo然后从模块a导入SomeClass
。如果我们用patch()
嘲笑a.SomeClass
那么它对我们的测试没有影响;模块b已经有了对real SomeClass
的引用,看起来我们的修补没有效果.
关键是要修补SomeClass
它被使用的地方(或被抬起的地方)。在这种情况下some_function
实际上会查找SomeClass
在模块b中,我们已导入它。修补应该如下所示:
@patch("b.SomeClass")
然而,考虑替代方案,而不是from a importSomeClass
模块b做import a
和some_function
使用a.SomeClass
。这两种导入形式都很常见。在这种情况下,我们要修补的类正在模块中查找,所以我们必须修补a.SomeClass
而不是:
@patch("a.SomeClass")
修补描述符和代理对象
这两个补丁和补丁.object正确修补和恢复描述符:classmethods,静态方法和属性。你应该在class而不是实例上修补它们。他们还使用some对象代理属性访问,就像django设置对象一样.
MagicMock和魔法支持
模拟魔术方法
Mock
支持嘲笑Python协议方法,也称为“魔术方法”。这允许模拟对象替换实现Python协议的容器或其他对象.
因为魔术方法的查找方式与普通方法不同[2],所以特别实现了这种支持。这意味着只支持特定的magicmethods。支持列表包括almost他们都是。如果您有任何遗漏,请告诉我们.
您可以通过将您感兴趣的方法设置为函数或模拟实例来模拟魔术方法。如果你正在使用一个函数那么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)
调用魔术方法不会出现在method_calls
,但它们被记录在mock_calls
.
支持的魔术方法的完整列表是:
__hash__
,__sizeof__
,__repr__
和__str__
__dir__
,__format__
和__subclasses__
__floor__
,__trunc__
和__ceil__
- 比较:
__lt__
,__gt__
,__le__
,__ge__
,__eq__
和__ne__
- 容器方法:
__getitem__
,__setitem__
,__delitem__
,__contains__
,__len__
,__iter__
,__reversed__
和__missing__
- 上下文管理器:
__enter__
和__exit__
- 一元数字方法:
__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__
存在以下方法但是not支持,因为它们要么在使用模拟,不能动态设置,或可能导致问题:
__getattr__
,__setattr__
,__init__
和__new__
__prepare__
,__instancecheck__
,__subclasscheck__
,__del__
魔术模拟
有两个MagicMock
变种:MagicMock
和NonCallableMagicMock
.
- class
unittest.mock.
MagicMock
(*args, **kw) -
MagicMock
是Mock
的子类,具有大多数魔术方法的默认实现。您可以使用MagicMock
而无需自己配置魔术方法.构造函数参数与
Mock
.
- class
unittest.mock.
NonCallableMagicMock
(*args, **kw) -
一个不可赎回的版本
MagicMock
.构造函数参数与
MagicMock
具有相同的含义,但return_value和side_effect除外,它们对非可调用的mock没有意义.
魔术方法设置为MagicMock
对象,所以你可以配置命令以通常的方式使用它们:
>>> mock = MagicMock()>>> mock[3] = "fish">>> mock.__setitem__.assert_called_with(3, "fish")>>> mock.__getitem__.return_value = "result">>> mock[2]"result"
默认情况下,许多协议方法都需要返回特定类型的对象。这些方法预先配置了默认返回值,如果您对返回值不感兴趣,可以使用它们而无需执行任何操作。如果你想改变默认值,你仍然可以手动set返回值.
Methods及其默认值:
__lt__
:NotImplemented__gt__
:NotImplemented__le__
:NotImplemented__ge__
:NotImplemented__int__
:1__contains__
:False__len__
:0__iter__
:iter([])__exit__
:错误__complex__
:1j__float__
:1.0__bool__
:真的__index__
:1__hash__
:默认哈希mock__str__
:模拟的默认str__sizeof__
:模拟的默认sizeof
例如:
>>> mock = MagicMock()>>> int(mock)1>>> len(mock)0>>> list(mock)[]>>> object() in mockFalse
两个相等的方法,__eq__()
和__ne__()
,是特殊的。他们使用side_effect
属性对身份进行默认的相等比较,除非你改变它们的返回值以返回别的东西:
>>> MagicMock() == 3False>>> MagicMock() != 3True>>> mock = MagicMock()>>> mock.__eq__.return_value = True>>> mock == 3True
返回值对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] | 魔法方法should在课堂上而不是实际上被抬起头来。不同版本的Python与应用thisrule不一致。支持的协议方法应该适用于所有支持的Python版本. |
[3] | 这个函数基本上是连接到类的,但每个Mock 实例都保持隔离其他. |
Helpers
sentinel
unittest.mock.
sentinel
-
sentinel
对象提供了一种方便的方式为您的测试提供独特的对象.按名称访问属性时,将按需创建属性。访问相同的属性将始终返回相同的对象。返回的对象有一个合理的repr,以便可以读取测试失败消息.
更改版本3.7:
sentinel
属性现在保留其身份copied
或pickled
.
有时在测试时,您需要测试特定对象是作为参数传递给另一个方法,还是返回。创建命名的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_objectsentinel.some_object
DEFAULT
call
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
对于在“链接调用”上进行断言特别有用。Achained调用是对一行代码的多次调用。这会在模拟上导致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)或(name,positional args,keyword args)取决于它的构造方式。当你自己构造它们时,这并不是特别有趣,但call
和Mock.call_args
, Mock.call_args_list
属性中的Mock.mock_calls
对象可以被内省以获得它们包含的单个元素.
call
和Mock.call_args
中的Mock.call_args_list
对象是(元素args,关键字args)的两元组,而call
对象Mock.mock_calls
,以及你自己构建的那些,是(名字,位置args,关键字args)的三元组.
您可以使用它们的“元组”来提取单个参数,以获得更复杂的内省和断言。位置参数是一个元组(如果没有位置参数,则为空元组),而keywordarguments是一个字典:
>>> 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
create_autospec
unittest.mock.
create_autospec
(spec, spec_set=False, instance=False, **kwargs)-
使用另一个对象作为规范创建一个模拟对象。在themock上的属性将使用spec对象上的相应属性作为thepecpec。
被模拟的函数或方法将检查其参数以确保使用正确的签名调用它们.
如果spec_set是
True
然后尝试设置spec对象中不存在的属性会引发AttributeError
.如果一个类被用作规范,则mock的返回值(类的实例)将具有相同的规范。您可以通过传递
instance=True
。如果mock的实例是可调用的,则返回的mockwill只能被调用.create_autospec()
也会获取传递给创建的mock的构造函数的任意关键字参数.
查看自动指定以获取如何使用create_autospec()
进行自动指定的示例和autospec参数patch()
.
ANY
unittest.mock.
ANY
有时您可能需要关于some关于嘲笑的论点,但要么不关心某些论点,要么单独从call_args
中取出它们并对它们做出更复杂的论述.
要忽略某些参数,您可以传入比较等于everything的对象。无论发生什么事情,对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(或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
.
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.7.1中更改:添加
__iter__()
到实现,以便迭代(例如在forloops中)正确消耗read_data.
使用open()
作为上下文管理器是一个很好的方法来确保你的文件处理正常关闭并变得普遍:
with open("/some/path", "w") as f: f.write("something")
问题是,即使你嘲笑open()
的调用它是returned object那个用作上下文管理器(并且__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
功能。它将模拟的theapi限制为原始对象(规范)的api,但它是递归的(懒惰地实现),因此模拟的属性只具有与规范属性相同的api。另外,模拟的函数/方法有同样的调用签名作为原始函数,因此如果它们被错误地调出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)
由于错字,你的测试可以默默无误地通过.
第二个问题是嘲弄更普遍。如果你重构一些你的代码,重命名成员等,任何仍在使用old api但是使用模拟代替真实对象仍然会通过。这意味着即使您的代码被破坏,您的测试也都可以通过.
请注意,这是您需要集成测试以及单元测试的另一个原因。单独测试所有东西都很好,但是如果你不测试你的单元如何“连接在一起”,那么测试可能已经抓住了很多错误的空间.
mock
已经提供了一个功能来帮助你这个叫做speccing。如果你使用一个类或实例作为模拟的spec
,那么你只能在真实类上存在的模拟上访问属性:
>>> from urllib import request>>> mock = Mock(spec=request.Request)>>> mock.assret_called_withTraceback (most recent call last): ...AttributeError: Mock object has no attribute "assret_called_with"
规范只适用于模拟本身,所以我们仍然与mock上的任何方法都有相同的问题:
>>> mock.has_data()<mock.Mock object at 0x...>>>> mock.has_data.assret_called_with()
自动显示解决了这个问题。您可以将autospec=True
传递给patch()
/ patch.object()
或使用create_autospec()
函数创建一个带有规范的amock。如果使用autospec=True
参数patch()
那么被替换的对象将被用作spec对象。因为指定是“懒惰地”完成的(规范是作为模拟的属性创建的),你可以将它用于非常复杂或深度嵌套的对象(如导入模块导入模块的模块),而不需要大的性能.
这是一个使用它的例子:
>>> from urllib import request>>> patcher = patch("__main__.request", autospec=True)>>> mock_request = patcher.start()>>> request is mock_requestTrue>>> 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
对象不可调用,因此实例化mymocked的返回值out request.Request
是一个不可调用的模拟器。根据规范,我们断言中的拼写错误会引发正确的错误:
>>> req.add_header("spam", "eggs")<MagicMock name="request.Request().add_header()" id="...">>>> req.add_header.assret_called_withTraceback (most recent call last): ...AttributeError: Mock object has no attribute "assret_called_with">>> req.add_header.assert_called_with("spam", "eggs")
在很多情况下,您只能将autospec=True
添加到现有的patch()
调用中然后再由于错别字和apichanges.
以及使用autospec通过patch()
有一个create_autospec()
用于直接创建自动调整的模拟:
>>> from urllib import request>>> mock_request = create_autospec(request)>>> mock_request.Request("foo", "bar")<NonCallableMagicMock name="mock.Request()" spec="Request" id="...">
这不是没有警告和限制,这就是为什么它不是默认行为。为了知道specs对象上可用的属性,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"
有几种不同的方法可以解决这个问题。最简单但不一定是最烦人的方法是在创建后简单地在mock上设置requiredattributes。仅仅因为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__()
然后为他们提供viaclass属性(当然在实例之间共享)也更快。例如。
class Something: a = 33
这带来了另一个问题。提供None
对于后来成为不同类型对象的成员.None
作为一个规范是没用的,因为它不会让你访问any它上面的属性或方法。作为None
是never作为aspec有用,并且可能表示通常属于其他类型的成员,autospec不会对设置为None
。这些只是普通的嘲笑(好吧 – MagicMocks):
>>> 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] | 这仅适用于类或已实例化的对象。Callinga模拟类创建一个模拟实例does not创建一个真实的实例。它只是属性查找 – 连同调用dir() – 完成了 |
密封模拟
unittest.mock.
seal
(mock)-
密封将在访问被密封的模拟属性或其已经递归模拟的任何属性时禁用自动创建模拟.
如果将具有名称或规范的模拟实例分配给attributeit,则不会在密封链中考虑。这样可以防止密封件固定模拟物体的一部分.
>>> 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.
评论被关闭。