unittest.mock
—- 模拟对象库
Added in version 3.3.
源代码: Lib/unittest/mock.py [https://github.com/python/cpython/tree/3.13/Lib/unittest/mock.py]
unittest.mock
是一个用于测试的Python库。它允许使用模拟对象来替换受测系统的一些部分,并对这些部分如何被使用进行断言判断。
unittest.mock
提供的 Mock
类,能在整个测试套件中模拟大量的方法。创建后,就可以断言调用了哪些方法/属性及其参数。还可以以常规方式指定返回值并设置所需的属性。
此外,mock 还提供了用于修补测试范围内模块和类级别属性的 patch()
装饰器,和用于创建独特对象的 sentinel
。 阅读 quick guide 中的案例了解如何使用 Mock
,MagicMock
和 patch()
。
mock 被设计为配合 unittest
使用且它是基于 'action -> assertion' 模式而非许多模拟框架所使用的 'record -> replay' 模式。
对于较早版本的 Python 有一个反向移植的 unittest.mock
,即在 PyPI 上可用的 mock [https://pypi.org/project/mock/]。
快速上手
当您访问对象时, 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 被调用时引发一个异常:
- >>> from unittest.mock import Mock
- >>> mock = Mock(side_effect=KeyError('foo'))
- >>> mock()
- Traceback (most recent call last): ...
- KeyError: 'foo'
- >>> values = {'a': 1, 'b': 2, 'c': 3}
- >>> def side_effect(arg):
- ... return values[arg]
- ...
- >>> mock.side_effect = side_effect
- >>> mock('a'), mock('b'), mock('c')
- (1, 2, 3)
- >>> mock.side_effect = [5, 4, 3, 2, 1]
- >>> mock(), mock(), mock()
- (5, 4, 3)
Mock 还可以通过其他方法配置和控制其行为。例如 mock 可以通过设置 spec 参数来从一个对象中获取其规格(specification)。如果访问 mock 的属性或方法不在 spec 中,会报 AttributeError
错误。
使用 patch()
装饰去/上下文管理器,可以更方便地测试一个模块下的类或对象。你指定的对象会在测试过程中替换成 mock (或者其他对象),测试结束后恢复。
- >>> from unittest.mock import patch
- >>> @patch('module.ClassName2')
- ... @patch('module.ClassName1')
- ... def test(MockClass1, MockClass2):
- ... module.ClassName1()
- ... module.ClassName2()
- ... assert MockClass1 is module.ClassName1
- ... assert MockClass2 is module.ClassName2
- ... assert MockClass1.called
- ... assert MockClass2.called
- ...
- >>> test()
备注
当你嵌套 patch 装饰器时,mock 将以执行顺序传递给装饰器函数(Python 装饰器正常顺序)。由于从下至上,因此在上面的示例中,首先 mock 传入的 module.ClassName1
。
在查找对象的名称空间中修补对象使用 patch()
。使用起来很简单,阅读 在哪里打补丁 来快速上手。
patch()
也可以 with 语句中使用上下文管理。
- >>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
- ... thing = ProductionClass()
- ... thing.method(1, 2, 3)
- ...
- >>> mock_method.assert_called_once_with(1, 2, 3)
还有一个 patch.dict()
用于在一定范围内设置字典中的值,并在测试结束时将字典恢复为其原始状态:
- >>> foo = {'key': 'value'}
- >>> original = foo.copy()
- >>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
- ... assert foo == {'newkey': 'newvalue'}
- ...
- >>> assert foo == original
Mock支持 Python 魔术方法 。使用模式方法最简单的方式是使用 MagicMock
class. 。它可以做如下事情:
- >>> mock = MagicMock()
- >>> mock.__str__.return_value = 'foobarbaz'
- >>> str(mock)
- 'foobarbaz'
- >>> mock.__str__.assert_called_with()
Mock 能指定函数(或其他 Mock 实例)为魔术方法,它们将被适当地调用。 MagicMock
是预先创建了所有魔术方法(所有有用的方法) 的 Mock 。
下面是一个使用了普通 Mock 类的魔术方法的例子
- >>> mock = Mock()
- >>> mock.__str__ = Mock(return_value='wheeeeee')
- >>> str(mock)
- 'wheeeeee'
使用 autospeccing 可以保证测试中的模拟对象与要替换的对象具有相同的api 。在 patch 中可以通过 autospec 参数实现自动推断,或者使用 create_autospec()
函数。自动推断会创建一个与要替换对象相同的属相和方法的模拟对象,并且任何函数和方法(包括构造函数)都具有与真实对象相同的调用签名。
这么做是为了因确保不当地使用 mock 导致与生产代码相同的失败:
- >>> from unittest.mock import create_autospec
- >>> def function(a, b, c):
- ... pass
- ...
- >>> mock_function = create_autospec(function, return_value='fishy')
- >>> mock_function(1, 2, 3)
- 'fishy'
- >>> mock_function.assert_called_once_with(1, 2, 3)
- >>> mock_function('wrong arguments')
- Traceback (most recent call last): ...
- TypeError: missing a required argument: 'b'
在类中使用 create_autospec()
时,会复制 __init__
方法的签名,另外在可调用对象上使用时,会复制 __call__
方法的签名。
Mock 类
Mock
是一个可以灵活的替换存根 (stubs) 的对象,可以测试所有代码。 Mock 是可调用的,在访问其属性时创建一个新的 mock [1] 。访问相同的属性只会返回相同的 mock 。 Mock 会保存调用记录,可以通过断言获悉代码的调用。
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
对象的行为:- spec: 可以是要给字符串列表,也可以是充当模拟对象规范的现有对象(类或实例)。如果传入一个对象,则通过在该对象上调用 dir 来生成字符串列表(不支持的魔法属性和方法除外)。访问不在此列表中的任何属性都将触发
AttributeError
。
- spec: 可以是要给字符串列表,也可以是充当模拟对象规范的现有对象(类或实例)。如果传入一个对象,则通过在该对象上调用 dir 来生成字符串列表(不支持的魔法属性和方法除外)。访问不在此列表中的任何属性都将触发
如果 spec 是一个对象(而不是字符串列表)则 __class__
将返回 spec 对象的类。 这允许 mock 通过 isinstance()
测试。
spec_set :spec 的更严格的变体。如果使用了该属性,尝试模拟 set 或 get 的属性不在 spec_set 所包含的对象中时,会抛出
AttributeError
。side_effect :每当调用 Mock 时都会调用的函数。 参见
side_effect
属性。 对于引发异常或动态更改返回值很有用。 该函数使用与 mock 函数相同的参数调用,并且除非返回DEFAULT
,否则该函数的返回值将用作返回值。
另外, side_effect 可以是异常类或实例。 此时,调用模拟程序时将引发异常。
如果 side_effect 是可迭代对象,则每次调用 mock 都将返回可迭代对象的下一个值。
设置 side_effect 为 None
即可清空。
return_value :调用 mock 的返回值。 默认情况下,是一个新的Mock(在首次访问时创建)。 参见
return_value
属性 。unsafe: 在默认情况下,访问任何名字以 assert, assret, asert, aseert 或 assrt 开头的属性都将引发
AttributeError
。 传入unsafe=True
将允许访问这些属性。
Added in version 3.5.
- wraps :要包装的 mock 对象。 如果 wraps 不是
None
,那么调用 Mock 会将调用传递给 wraps 的对象(返回实际结果)。 对模拟的属性访问将返回一个 Mock 对象,该对象包装了 wraps 对象的相应属性(因此,尝试访问不存在的属性将引发AttributeError
)。
如果明确指定 return_value ,则调用时不会返回包装对象,而是返回 return_value 。
- name :mock 的名称。 在调试时很有用。 名称会传递到子 mock 。
还可以使用任意关键字参数来调用 mock 。 创建模拟后,将使用这些属性来设置 mock 的属性。 有关详细信息,请参见 configure_mock()
方法。
- assert_called()
- 断言 mock 已被调用至少一次。
- >>> mock = Mock()
- >>> mock.method()
- <Mock name='mock.method()' id='...'>
- >>> mock.method.assert_called()
Added in version 3.6.
- assert_called_once()
- 断言 mock 已被调用恰好一次。
- >>> mock = Mock()
- >>> mock.method()
- <Mock name='mock.method()' id='...'>
- >>> mock.method.assert_called_once()
- >>> mock.method()
- <Mock name='mock.method()' id='...'>
- >>> mock.method.assert_called_once()
- Traceback (most recent call last):
- ...
- AssertionError: Expected 'method' to have been called once. Called 2 times.
- Calls: [call(), call()].
Added in version 3.6.
- assert_called_with(args, *kwargs)
- 此方法是断言上次调用已以特定方式进行的一种便捷方法:
- >>> mock = Mock()
- >>> mock.method(1, 2, 3, test='wow')
- <Mock name='mock.method()' id='...'>
- >>> mock.method.assert_called_with(1, 2, 3, test='wow')
- assert_called_once_with(args, *kwargs)
- 断言 mock 已被调用恰好一次,并且向该调用传入了指定的参数。
- >>> mock = Mock(return_value=None)
- >>> mock('foo', bar='baz')
- >>> mock.assert_called_once_with('foo', bar='baz')
- >>> mock('other', bar='values')
- >>> mock.assert_called_once_with('other', bar='values')
- Traceback (most recent call last): ...
- AssertionError: Expected 'mock' to be called once. Called 2 times.
- Calls: [call('foo', bar='baz'), call('other', bar='values')].
- assert_any_call(args, *kwargs)
- 断言 mock 已被调用并附带了指定的参数。
如果 mock 曾经 被调用过则断言通过,不同于 assert_called_with()
和 assert_called_once_with()
那样只有在调用是最近的一次时才会通过,而对于 assert_called_once_with()
则它还必须是唯一的一次调用。
- >>> mock = Mock(return_value=None)
- >>> mock(1, 2, arg='thing')
- >>> mock('some', 'thing', 'else')
- >>> mock.assert_any_call(1, 2, arg='thing')
- assert_has_calls(calls, any_order=False)
- 断言 mock 已附带指定的参数被调用。 将针对这些调用检查
mock_calls
列表。
如果 any_order 为假值则调用必须是顺序进行的。 在指定的调用之前或之后还可以有额外的调用。
如果 any_order 为真值则调用可以是任意顺序的,但它们都必须在 mock_calls
中出现。
- >>> mock = Mock(return_value=None)
- >>> mock(1)
- >>> mock(2)
- >>> mock(3)
- >>> mock(4)
- >>> calls = [call(2), call(3)]
- >>> mock.assert_has_calls(calls)
- >>> calls = [call(4), call(2), call(3)]
- >>> mock.assert_has_calls(calls, any_order=True)
- assert_not_called()
- 断言 mock 从未被调用过。
- >>> m = Mock()
- >>> m.hello.assert_not_called()
- >>> obj = m.hello()
- >>> m.hello.assert_not_called()
- Traceback (most recent call last): ...
- AssertionError: Expected 'hello' to not have been called. Called 1 times.
- Calls: [call()].
Added in version 3.5.
- reset_mock(*, return_value=False, side_effect=False)
- reset_mock 方法将在 mock 对象上重围所有的调用属性:
- >>> mock = Mock(return_value=None)
- >>> mock('hello')
- >>> mock.called
- True
- >>> mock.reset_mock()
- >>> mock.called
- False
在 3.6 版本发生变更: 向 reset_mock 函数添加了两个仅限关键字参数。
这在你想要创建一系列重用同一对象的断言的场合会很有用处。 请注意 reset_mock()
不会 清除 return_value
, side_effect
或任何你使用普通赋值所默认设置的子属性。 如果你想要重置 return_value
或 side_effect
,则要为相应的形参传入 True
。 子 mock 和返回值 mock (如果有的话) 也会被重置。
备注
return_value 和 side_effect 均为仅限关键字参数。
- mock_add_spec(spec, spec_set=False)
- 为 mock 添加描述。 spec 可以是一个对象或字符串列表。 只有 spec 上的属性可以作为来自 mock 的属性被获取。
如果 spec_set 为真值则只有 spec 上的属性可以被设置。
- attach_mock(mock, attribute)
附加一个 mock 作为这一个的属性,替换它的名称和上级。 对附加 mock 的调用将记录在这一个的
method_calls
和mock_calls
属性中。configure_mock(**kwargs)
- 通过关键字参数在 mock 上设置属性。
属性加返回值和附带影响可以使用标准点号标记在子 mock 上设置并在方法调用中解包一个字典:
- >>> mock = Mock()
- >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
- >>> mock.configure_mock(**attrs)
- >>> mock.method()
- 3
- >>> mock.other()
- Traceback (most recent call last): ...
- KeyError
同样的操作可在对 mock 的构造器调用中达成:
- >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
- >>> mock = Mock(some_attribute='eggs', **attrs)
- >>> mock.some_attribute
- 'eggs'
- >>> mock.method()
- 3
- >>> mock.other()
- Traceback (most recent call last): ...
- KeyError
configure_mock()
的存在是使得 mock 被创建之后的配置更为容易。
- dir()
Mock
对象会将dir(some_mock)
的结果限制为有用结果。 对于带有 spec 的 mock 这还包括 mock 的所有被允许的属性。
请查看 FILTER_DIR
了解此过滤做了什么,以及如何停用它。
- getchild_mock(**kw)
- 创建针对属性和返回值的子 mock。 默认情况下子 mock 将为与其上级相同的类型。 Mock 的子类可能需要重载它来定制子 mock 的创建方式。
对于非可调用的 mock 将会使用可调用的变化形式(而非不是任意的自定义子类)。
- called
- 一个表示 mock 对象是否已被调用的布尔值:
- >>> mock = Mock(return_value=None)
- >>> mock.called
- False
- >>> mock()
- >>> mock.called
- True
- call_count
- 一个告诉你 mock 对象已被调用多少次的整数值:
- >>> mock = Mock(return_value=None)
- >>> mock.call_count
- 0
- >>> mock()
- >>> mock()
- >>> mock.call_count
- 2
- return_value
- 设置这个来配置通过调用该 mock 所返回的值:
- >>> mock = Mock()
- >>> mock.return_value = 'fish'
- >>> mock()
- 'fish'
默认的返回值是一个 mock 对象并且你可以通过正常方式来配置它:
- >>> mock = Mock()
- >>> mock.return_value.attribute = sentinel.Attribute
- >>> mock.return_value()
- <Mock name='mock()()' id='...'>
- >>> mock.return_value.assert_called_with()
return_value
也可以在构造器中设置:
- >>> mock = Mock(return_value=3)
- >>> mock.return_value
- 3
- >>> mock()
- 3
- side_effect
- 这可以是当该This can either be a function to be called when the mock 被调用时将被调用的一个函数,可调用对象或者要被引发的异常(类或实例)。
如果你传入一个函数则它将附带与该 mock 相同的参数被调用并且除了该函数返回 DEFAULT
单例的情况以外对该 mock 的调用都将随后返回该函数所返回的任何东西。 如果该函数返回 DEFAULT
则该 mock 将返回其正常值 (来自 return_value
)。
如果你传入一个可迭代对象,它会被用来获取一个在每次调用时必须产生一个值的迭代器。 这个值可以是一个要被引发的异常实例,或是一个要从该调用返回给 mock 的值 (DEFAULT
处理与函数的情况一致)。
一个引发异常(来测试 API 的异常处理)的 mock 的示例:
- >>> mock = Mock()
- >>> mock.side_effect = Exception('Boom!')
- >>> mock()
- Traceback (most recent call last): ...
- Exception: Boom!
使用 side_effect
来返回包含多个值的序列:
- >>> mock = Mock()
- >>> mock.side_effect = [3, 2, 1]
- >>> mock(), mock(), mock()
- (3, 2, 1)
使用一个可调用对象:
- >>> mock = Mock(return_value=3)
- >>> def side_effect(*args, **kwargs):
- ... return DEFAULT
- ...
- >>> mock.side_effect = side_effect
- >>> mock()
- 3
side_effect
可以在构造器中设置。 下面是在 mock 被调用时增加一个该属性值并返回它的例子:
- >>> side_effect = lambda value: value + 1
- >>> mock = Mock(side_effect=side_effect)
- >>> mock(3)
- 4
- >>> mock(-8)
- -7
将 side_effect
设为 None
可以清除它:
- >>> m = Mock(side_effect=KeyError, return_value=3)
- >>> m()
- Traceback (most recent call last): ...
- KeyError
- >>> m.side_effect = None
- >>> m()
- 3
- call_args
- 这可以是
None
(如果 mock 没有被调用),或是 mock 最近一次被调用时附带的参数。 这将采用元组的形式:第一个成员也可以通过args
特征属性来访问,它是 mock 被调用时所附带的任何位置参数 (或为空元组),而第二个成员也可以通过kwargs
特征属性来访问,它则是任何关键字参数 (或为空字典)。
- >>> mock = Mock(return_value=None)
- >>> print(mock.call_args)
- None
- >>> mock()
- >>> mock.call_args
- call()
- >>> mock.call_args == ()
- True
- >>> mock(3, 4)
- >>> mock.call_args
- call(3, 4)
- >>> mock.call_args == ((3, 4),)
- True
- >>> mock.call_args.args
- (3, 4)
- >>> mock.call_args.kwargs
- {}
- >>> mock(3, 4, 5, key='fish', next='w00t!')
- >>> mock.call_args
- call(3, 4, 5, key='fish', next='w00t!')
- >>> mock.call_args.args
- (3, 4, 5)
- >>> mock.call_args.kwargs
- {'key': 'fish', 'next': 'w00t!'}
call_args
,以及列表 call_args_list
, method_calls
和 mock_calls
的成员都是 call
对象。 这些对象属性元组,因此它们可被解包以获得单独的参数并创建更复杂的断言。 参见 作为元组的 call。
在 3.8 版本发生变更: 增加了 args
和 kwargs
特征属性。properties.
- call_args_list
- 这是一个已排序的对 mock 对象的所有调用的列表(因此该列表的长度就是它已被调用的次数)。 在执行任何调用之前它将是一个空列表。
call
对象可以被用来方便地构造调用列表以与call_args_list
相比较。
- >>> mock = Mock(return_value=None)
- >>> mock()
- >>> mock(3, 4)
- >>> mock(key='fish', next='w00t!')
- >>> mock.call_args_list
- [call(), call(3, 4), call(key='fish', next='w00t!')]
- >>> expected = [(), ((3, 4),), ({'key': 'fish', 'next': 'w00t!'},)]
- >>> mock.call_args_list == expected
- True
call_args_list
的成员均为 call
对象。 它们可作为元组被解包以获得单个参数。 参见 作为元组的 call。
- method_calls
- 除了会追踪对其自身的调用,mock 还会追踪对方法和属性,以及 它们的 方法和属性的访问:
- >>> mock = Mock()
- >>> mock.method()
- <Mock name='mock.method()' id='...'>
- >>> mock.property.method.attribute()
- <Mock name='mock.property.method.attribute()' id='...'>
- >>> mock.method_calls
- [call.method(), call.property.method.attribute()]
method_calls
的成员均为 call
对象。 它们可以作为元组被解包以获得单个参数。 参见 作为元组的 call。
- mock_calls
mock_calls
会记录 所有 对 mock 对象、它的方法、魔术方法的调用 以及 返回值的 mock。
- >>> mock = MagicMock()
- >>> result = mock(1, 2, 3)
- >>> mock.first(a=3)
- <MagicMock name='mock.first()' id='...'>
- >>> mock.second()
- <MagicMock name='mock.second()' id='...'>
- >>> int(mock)
- 1
- >>> result(1)
- <MagicMock name='mock()()' id='...'>
- >>> expected = [call(1, 2, 3), call.first(a=3), call.second(),
- ... call.__int__(), call()(1)]
- >>> mock.mock_calls == expected
- True
mock_calls
的成员均为 call
对象。 它们可以作为元组被解包以获得单个参数。 参见 作为元组的 call。
备注
mock_calls
的记录方式意味着在进行嵌套调用时,之前调用的形参不会被记录因而这样的比较将总是相等:
- >>> mock = MagicMock()
- >>> mock.top(a=3).bottom()
- <MagicMock name='mock.top().bottom()' id='...'>
- >>> mock.mock_calls
- [call.top(a=3), call.top().bottom()]
- >>> mock.mock_calls[-1] == call.top(a=-1).bottom()
- True
- class
- 通常一个对象的
__class__
属性将返回其类型。 对于具有spec
的 mock 对象,__class__
将改为返回 spec 类。 这允许 mock 为它们所替换 / 屏蔽的对象跳过isinstance()
测试:
- >>> mock = Mock(spec=3)
- >>> isinstance(mock, int)
- True
__class__
是可以被赋值的,这允许 mock 跳过 isinstance()
检测而不强制要求你使用 spec:
- >>> mock = Mock()
- >>> mock.__class__ = dict
- >>> isinstance(mock, dict)
- True
- class unittest.mock.NonCallableMock(spec=None, wraps=None, name=None, spec_set=None, **kwargs)
- 不可调用的
Mock
版本。 其构造器的形参具有与Mock
相同的含义,区别在于 return_value 和 side_effect 在不可调用的 mock 上没有意义。
使用一个类或实例作为 spec
或 spec_set
的对象能够跳过 isinstance()
测试:
- >>> mock = Mock(spec=SomeClass)
- >>> isinstance(mock, SomeClass)
- True
- >>> mock = Mock(spec_set=SomeClass())
- >>> isinstance(mock, SomeClass)
- True
Mock
类具有对 mock 操作魔术方法的支持。 请参阅 魔术方法 了解完整细节。
mock 操作类和 patch()
装饰器都接受任意关键字参数用于配置。 对于 patch()
装饰器来说关键字参数会被传给所创建 mock 的构造器。 这些关键字被用于配置 mock 的属性:
- >>> m = MagicMock(attribute=3, other='fish')
- >>> m.attribute
- 3
- >>> m.other
- 'fish'
子 mock 的返回值和附带效果也可使用带点号的标记通过相同的方式来设置。 由于你无法直接在调用中使用带点号的名称因此你需要创建一个字典并使用 **
来解包它:
- >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
- >>> mock = Mock(some_attribute='eggs', **attrs)
- >>> mock.some_attribute
- 'eggs'
- >>> mock.method()
- 3
- >>> mock.other()
- Traceback (most recent call last): ...
- KeyError
使用 spec (或 spec_set) 创建的可调用 mock 将在匹配调用与 mock 时内省规格说明对象的签名。 因此,它可以匹配实际调用的参数而不必关心它们是按位置还是按名称传入的:
- >>> def f(a, b, c): pass
- ...
- >>> mock = Mock(spec=f)
- >>> mock(1, 2, c=3)
- <Mock name='mock()' id='140161580456576'>
- >>> mock.assert_called_with(1, 2, 3)
- >>> mock.assert_called_with(a=1, b=2, c=3)
这适用于 assert_called_with()
, assert_called_once_with()
, assert_has_calls()
和 assert_any_call()
。 当执行 自动 spec 时,它还将应用于 mock 对象的方法调用。
在 3.4 版本发生变更: 添加了在附带规格说明和自动规格说明的 mock 对象上的签名内省
- class unittest.mock.PropertyMock(args, *kwargs)
- 旨在作为类的
property
或其他 descriptor 的 mock。PropertyMock
提供了_get_()
和__set__()
方法以便你可以在它被提取时指定一个返回值。
当从一个对象提取 PropertyMock
实例时将不附带任何参数地调用该 mock。 如果设置它则调用该 mock 时将附带被设置的值。
- >>> class Foo:
- ... @property
- ... def foo(self):
- ... return 'something'
- ... @foo.setter
- ... def foo(self, value):
- ... pass
- ...
- >>> with patch('__main__.Foo.foo', new_callable=PropertyMock) as mock_foo:
- ... mock_foo.return_value = 'mockity-mock'
- ... this_foo = Foo()
- ... print(this_foo.foo)
- ... this_foo.foo = 6
- ...
- mockity-mock
- >>> mock_foo.mock_calls
- [call(), call(6)]
由于 mock 属性的存储方式你无法直接将 PropertyMock
附加到一个 mock 对象。 但是你可以将它附加到 mock 类型对象:
- >>> m = MagicMock()
- >>> p = PropertyMock(return_value=3)
- >>> type(m).foo = p
- >>> m.foo
- 3
- >>> p.assert_called_once_with()
小心
如果由 PropertyMock
引发了 AttributeError
,它将被解读为缺少描述器并将在父 mock 上调用 __getattr__()
:
- >>> m = MagicMock()
- >>> no_attribute = PropertyMock(side_effect=AttributeError)
- >>> type(m).my_property = no_attribute
- >>> m.my_property
- <MagicMock name='mock.my_property' id='140165240345424'>
请参阅 __getattr__()
了解详情。
- 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
是一个可迭代对象,则异步函数将返回该可迭代对象的下一个值,但是,如果结果序列被耗尽,则会立即引发StopAsyncIteration
,如果
side_effect
未被定义,则异步函数将返回is not defined, the async function will return the value defined byreturn_value
所定义的值,因而,在默认情况下,异步函数会返回一个新的AsyncMock
对象。
将 Mock
或 MagicMock
的 spec 设为异步函数将导致在调用后返回一个协程对象。
- >>> async def async_func(): pass
- ...
- >>> mock = MagicMock(async_func)
- >>> mock
- <MagicMock spec='function' id='...'>
- >>> mock()
- <coroutine object AsyncMockMixin._mock_call at ...>
将 Mock
, MagicMock
或 AsyncMock
的 spec 设为带有异步和同步函数的类将自动删除其中的同步函数并将它们设为 MagicMock
(如果上级 mock 是 AsyncMock
或 MagicMock
) 或者 Mock
(如果上级 mock 是 Mock
)。 所有异步函数都将为 AsyncMock
。
- >>> class ExampleClass:
- ... def sync_foo():
- ... pass
- ... async def async_foo():
- ... pass
- ...
- >>> a_mock = AsyncMock(ExampleClass)
- >>> a_mock.sync_foo
- <MagicMock name='mock.sync_foo' id='...'>
- >>> a_mock.async_foo
- <AsyncMock name='mock.async_foo' id='...'>
- >>> mock = Mock(ExampleClass)
- >>> mock.sync_foo
- <Mock name='mock.sync_foo' id='...'>
- >>> mock.async_foo
- <AsyncMock name='mock.async_foo' id='...'>
Added in version 3.8.
- assert_awaited()
- 断言 mock 已被等待至少一次。 请注意这是从被调用的对象中分离出来的,必须要使用
await
关键字:
- >>> mock = AsyncMock()
- >>> async def main(coroutine_mock):
- ... await coroutine_mock
- ...
- >>> coroutine_mock = mock()
- >>> mock.called
- True
- >>> mock.assert_awaited()
- Traceback (most recent call last):
- ...
- AssertionError: Expected mock to have been awaited.
- >>> asyncio.run(main(coroutine_mock))
- >>> mock.assert_awaited()
- assert_awaited_once()
- 断言 mock 已被等待恰好一次。
- >>> mock = AsyncMock()
- >>> async def main():
- ... await mock()
- ...
- >>> asyncio.run(main())
- >>> mock.assert_awaited_once()
- >>> asyncio.run(main())
- >>> mock.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 await not found.
- Expected: mock('other')
- Actual: mock('foo', bar='bar')
- assert_awaited_once_with(args, *kwargs)
- 断言 mock 已被等待恰好一次并且附带了指定的参数。
- >>> mock = AsyncMock()
- >>> async def main(*args, **kwargs):
- ... await mock(*args, **kwargs)
- ...
- >>> asyncio.run(main('foo', bar='bar'))
- >>> mock.assert_awaited_once_with('foo', bar='bar')
- >>> asyncio.run(main('foo', bar='bar'))
- >>> mock.assert_awaited_once_with('foo', bar='bar')
- Traceback (most recent call last):
- ...
- AssertionError: Expected mock to have been awaited once. Awaited 2 times.
- assert_any_await(args, *kwargs)
- 断言 mock 已附带了指定的参数被等待。
- >>> mock = AsyncMock()
- >>> async def main(*args, **kwargs):
- ... await mock(*args, **kwargs)
- ...
- >>> asyncio.run(main('foo', bar='bar'))
- >>> asyncio.run(main('hello'))
- >>> mock.assert_any_await('foo', bar='bar')
- >>> mock.assert_any_await('other')
- Traceback (most recent call last):
- ...
- AssertionError: mock('other') await not found
- assert_has_awaits(calls, any_order=False)
- 断言 mock 已附带了指定的调用被等待。 将针对这些等待检查
await_args_list
列表。
如果 any_order 为假值则等待必须是顺序进行的。 在指定的等待之前或之后还可以有额外的调用。
如果 any_order 为真值则等待可以是任意顺序的,但它们都必须在 await_args_list
中出现。
- >>> mock = AsyncMock()
- >>> async def main(*args, **kwargs):
- ... await mock(*args, **kwargs)
- ...
- >>> calls = [call("foo"), call("bar")]
- >>> mock.assert_has_awaits(calls)
- Traceback (most recent call last):
- ...
- AssertionError: Awaits not found.
- Expected: [call('foo'), call('bar')]
- Actual: []
- >>> asyncio.run(main('foo'))
- >>> asyncio.run(main('bar'))
- >>> mock.assert_has_awaits(calls)
- assert_not_awaited()
- 断言 mock 从未被等待过。
- >>> mock = AsyncMock()
- >>> mock.assert_not_awaited()
- reset_mock(args, *kwargs)
参见
Mock.reset_mock()
。 还会将await_count
设为 0,将await_args
设为 None,并清空await_args_list
。await_count
- 一个追踪 mock 对象已被等待多少次的整数值。
- >>> mock = AsyncMock()
- >>> async def main():
- ... await mock()
- ...
- >>> asyncio.run(main())
- >>> mock.await_count
- 1
- >>> asyncio.run(main())
- >>> mock.await_count
- 2
- await_args
- 这可能为
None
(如果 mock 从未被等待),或为该 mock 上一次被等待所附带的参数。 其功能与Mock.call_args
相同。
- >>> mock = AsyncMock()
- >>> async def main(*args):
- ... await mock(*args)
- ...
- >>> mock.await_args
- >>> asyncio.run(main('foo'))
- >>> mock.await_args
- call('foo')
- >>> asyncio.run(main('bar'))
- >>> mock.await_args
- call('bar')
- await_args_list
- 这是由对 mock 对象按顺序执行的所有等待组成的列表(因此该列表的长度即它被等待的次数)。 在有任何等待被执行之前它将为一个空列表。
- >>> mock = AsyncMock()
- >>> async def main(*args):
- ... await mock(*args)
- ...
- >>> mock.await_args_list
- []
- >>> asyncio.run(main('foo'))
- >>> mock.await_args_list
- [call('foo')]
- >>> asyncio.run(main('bar'))
- >>> mock.await_args_list
- [call('foo'), call('bar')]
- class unittest.mock.ThreadingMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, , timeout=UNSET, *kwargs)
- 针对多线程测试的
MagicMock
版本。ThreadingMock
对象提供了额外的方法用来等待调用被唤起,而不是立即对其执行断言。
默认的超时值由 timeout
参数指定,或者由 ThreadingMock.DEFAULT_TIMEOUT
属性来重置,该属性默认为阻塞型 (None
)。
你可以通过设置 ThreadingMock.DEFAULT_TIMEOUT
来配置全局默认超时。
- wait_until_called(*, timeout=UNSET)
- 等待直到 mock 被调用。
如果在创建 mock 时传入了 timeout 值或者如果向该函数传入了 timeout 参数,那么当调用未在时限内执行完毕则会引发 AssertionError
。
- >>> mock = ThreadingMock()
- >>> thread = threading.Thread(target=mock)
- >>> thread.start()
- >>> mock.wait_until_called(timeout=1)
- >>> thread.join()
- wait_until_any_call_with(args, *kwargs)
- 等待直到该 mock 附带指定参数被调用。
如果在创建该 mock 时传入了 timeout 值则当调用未在时限内执行完成则会引发 AssertionError
。
- >>> mock = ThreadingMock()
- >>> thread = threading.Thread(target=mock, args=("arg1", "arg2",), kwargs={"arg": "thing"})
- >>> thread.start()
- >>> mock.wait_until_any_call_with("arg1", "arg2", arg="thing")
- >>> thread.join()
- DEFAULT_TIMEOUT
- 创建
ThreadingMock
实例的全局默认超时秒数。
Added in version 3.13.
调用
Mock 对象是可调用对象。 调用将把值集合作为 return_value
属性返回。 默认的返回值是一个新的 Mock 对象;它会在对返回值的首次访问(不论是显式访问还是通过调用 Mock)时被创建 —— 但它会被保存并且每次都返回相同的对象。
对该对象的调用将被记录在 call_args
和 call_args_list
等属性中。
如果设置了 side_effect
则它将在调用被记录之后被调用,因此如果 side_effect
引发了异常该调用仍然会被记录。
让一个 mock 在被调用时引发异常的最简单方式是将 side_effect
设为一个异常类或实例:
- >>> m = MagicMock(side_effect=IndexError)
- >>> m(1, 2, 3)
- Traceback (most recent call last): ...
- IndexError
- >>> m.mock_calls
- [call(1, 2, 3)]
- >>> m.side_effect = KeyError('Bang!')
- >>> m('two', 'three', 'four')
- Traceback (most recent call last): ...
- KeyError: 'Bang!'
- >>> m.mock_calls
- [call(1, 2, 3), call('two', 'three', 'four')]
如果 side_effect
是一个函数则该函数所返回的对象就是调用该 mock 所返回的对象。 side_effect
函数在被调用时将附带与该 mock 相同的参数。 这允许你根据输入动态地改变返回值:
- >>> def side_effect(value):
- ... return value + 1
- ...
- >>> m = MagicMock(side_effect=side_effect)
- >>> m(1)
- 2
- >>> m(2)
- 3
- >>> m.mock_calls
- [call(1), call(2)]
如果你想让该 mock 仍然返回默认的返回值(一个新的 mock 对象),或是任何设定的返回值,那么有两种方式可以做到这一点。 从 side_effect
内部返回 return_value
,或者返回 DEFAULT
:
- >>> m = MagicMock()
- >>> def side_effect(*args, **kwargs):
- ... return m.return_value
- ...
- >>> m.side_effect = side_effect
- >>> m.return_value = 3
- >>> m()
- 3
- >>> def side_effect(*args, **kwargs):
- ... return DEFAULT
- ...
- >>> m.side_effect = side_effect
- >>> m()
- 3
要移除一个 side_effect
,并返回到默认行为,请将 side_effect
设为 None
:
- >>> m = MagicMock(return_value=6)
- >>> def side_effect(*args, **kwargs):
- ... return 3
- ...
- >>> m.side_effect = side_effect
- >>> m()
- 3
- >>> m.side_effect = None
- >>> m()
- 6
side_effect
也可以是任意可迭代对象。 对该 mock 的重复调用将返回来自该可迭代对象的值(直到该可迭代对象被耗尽并导致 StopIteration
被引发):
- >>> m = MagicMock(side_effect=[1, 2, 3])
- >>> m()
- 1
- >>> m()
- 2
- >>> m()
- 3
- >>> m()
- Traceback (most recent call last): ...
- StopIteration
如果该可迭代对象有任何成员属于异常则它们将被引发而不是被返回:
- >>> iterable = (33, ValueError, 66)
- >>> m = MagicMock(side_effect=iterable)
- >>> m()
- 33
- >>> m()
- Traceback (most recent call last): ...
- ValueError
- >>> m()
- 66
删除属性
Mock 对象会根据需要创建属性。 这允许它们可以假装成任意类型的对象。
你可能想要一个 mock 对象在调用 hasattr()
时返回 False
,或者在获取某个属性时引发 AttributeError
。 你可以通过提供一个对象作为 mock 的 spec
来做到这点,但这并不总是很方便。
你可以通过删除属性来“屏蔽”它们。 属性一旦被删除,访问它将引发 AttributeError
。
- >>> mock = MagicMock()
- >>> hasattr(mock, 'm')
- True
- >>> del mock.m
- >>> hasattr(mock, 'm')
- False
- >>> del mock.f
- >>> mock.f
- Traceback (most recent call last): ...
- AttributeError: f
Mock 的名称与 name 属性
由于 "name" 是 Mock
构造器的参数之一,如果你想让你的 mock 对象具有 "name" 属性你不可以在创建时传入该参数。 有两个替代方式。 一个选项是使用 configure_mock()
:
- >>> mock = MagicMock()
- >>> mock.configure_mock(name='my_name')
- >>> mock.name
- 'my_name'
一个更简单的选项是在 mock 创建之后简单地设置 "name" 属性:
- >>> mock = MagicMock()
- >>> mock.name = "foo"
附加 Mock 作为属性
当你附加一个 mock 作为另一个 mock 的属性(或作为返回值)时它会成为该 mock 的 "子对象"。 对子对象的调用会被记录在父对象的 method_calls
和 mock_calls
属性中。 这适用于配置子 mock 然后将它们附加到父对象,或是将 mock 附加到将记录所有对子对象的调用的父对象上并允许你创建有关 mock 之间的调用顺序的断言:
- >>> parent = MagicMock()
- >>> child1 = MagicMock(return_value=None)
- >>> child2 = MagicMock(return_value=None)
- >>> parent.child1 = child1
- >>> parent.child2 = child2
- >>> child1(1)
- >>> child2(2)
- >>> parent.mock_calls
- [call.child1(1), call.child2(2)]
这里有一个例外情况是如果 mock 设置了名称。 这允许你在出于某些理由不希望其发生时避免 "父对象" 的影响。
- >>> mock = MagicMock()
- >>> not_a_child = MagicMock(name='not-a-child')
- >>> mock.attribute = not_a_child
- >>> mock.attribute()
- <MagicMock name='not-a-child()' id='...'>
- >>> mock.mock_calls
- []
通过 patch()
创建的 mock 会被自动赋予名称。 要将具有名称的 mock 附加到父对象上你应当使用 attach_mock()
方法:
- >>> thing1 = object()
- >>> thing2 = object()
- >>> parent = MagicMock()
- >>> with patch('__main__.thing1', return_value=None) as child1:
- ... with patch('__main__.thing2', return_value=None) as child2:
- ... parent.attach_mock(child1, 'child1')
- ... parent.attach_mock(child2, 'child2')
- ... child1('one')
- ... child2('two')
- ...
- >>> parent.mock_calls
- [call.child1('one'), call.child2('two')]
[1]
仅有的例外是魔术方法和属性(其名称前后都带有双下划线)。 Mock 不会创建它们而是将引发 AttributeError
。 这是因为解释器将会经常隐式地请求这些方法,并且在它准备接受一个魔术方法却得到一个新的 Mock 对象时会 相当 困惑。 如果你需要魔术方法支持请参阅 魔术方法。
patch 装饰器
patch 装饰器仅被用于在它们所装饰的函数作用域内部为对象添加补丁。 它们会自动为你执行去除补丁的处理,即使是在引发了异常的情况下。 所有这些函数都还可在 with 语句中使用或是作为类装饰器。
patch
备注
问题的关键是要在正确的命名空间中打补丁。 参见 where to patch 一节。
- unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)
patch()
可以作为函数装饰器、类装饰器或上下文管理器。 在函数或 with 语句的内部,target 会打上一个 new 对象补丁。 当函数/with 语句退出时补丁将被撤销。
如果 new 被省略,那么如果被打补丁的对象是一个异步函数则 target 将被替换为 AsyncMock
否则替换为 MagicMock
。 如果 patch()
被用作装饰器并且 new 被省略,那么已创建的 mock 将作为一个附加参数传入被装饰的函数。 如果 patch()
被用作上下文管理器那么已创建的 mock 将被该上下文管理器所返回。
target 应当为 'package.module.ClassName'
形式的字符串。 target 将被导入并且该指定对象会被替换为 new 对象,因此 target 必须是可以从你调用 patch()
的环境中导入的。 target 会在被装饰的函数被执行的时候被导入,而非在装饰的时候。
spec 和 spec_set 关键字参数会被传递给 MagicMock
,如果 patch 为你创建了此对象的话。
此外你还可以传入 spec=True
或 spec_set=True
,这将使 patch 将被模拟的对象作为 spec/spec_set 对象传入。
new_callable 允许你指定一个不同的类,或者可调用对象,它将被调用以创建 新的 对象。 在默认情况下将指定 AsyncMock
用于异步函数,MagicMock
用于其他函数。
另一种更强形式的 spec 是 autospec。 如果你设置了 autospec=True
则将以来自被替换对象的 spec 来创建 mock。 mock 的所有属性也将具有被替换对象相应属性的 spec。 被模拟的方法和函数将检查它们的参数并且如果使用了错误的签名调用它们则将引发 TypeError
。 对于替换了一个类的 mock,它们的返回值(即‘实例’)将具有与该类相同的 spec。 请参阅 create_autospec()
函数以及 自动 spec。
除了 autospec=True
你还可以传入 autospec=some_object
以使用任意对象而不是被替换的对象作为 spec。
在默认情况下 patch()
将无法替换不存在的属性。 如果你传入 create=True
,且该属性并不存在,则 patch 将在调用被打补丁的函数时为你创建该属性,并在退出被打补丁的函数时再次删除它。 这适用于编写针对生产代码在运行时创建的属性的测试。 它默认是被关闭的因为这具有危险性。 当它被开启时你将能够针对实际上并不存在的 API 编写通过测试!
备注
在 3.5 版本发生变更: 如果你要给某个模块的内置函数打补丁则不必传入 create=True
,它默认就会被添加。
Patch 可以被用作 TestCase
类装饰器。 它是通过装饰类中的每个测试方法来发挥作用的。 当你的测试方法共享同一个补丁集时这将减少模板代码。 patch()
会通过查找以 patch.TEST_PREFIX
打头的名称来找到测试。 其默认值为 'test'
,这与 unittest
找到测试的方式一致。 你可以通过设置 patch.TEST_PREFIX
来指定其他的前缀。
Patch 可以通过 with 语句作为上下文管理器使用。 这时补丁将应用于 with 语句的缩进代码块。 如果你使用了 "as" 则打补丁的对象将被绑定到 "as" 之后的名称;这非常适用于当 patch()
为你创建 mock 对象的情况。
patch()
可接受任意关键字参数。 如果打补丁的对象是异步的则这些参数将被传给 AsyncMock
,否则传给 MagicMock
,或者是指定的 new_callable。
patch.dict(…)
, patch.multiple(…)
和 patch.object(…)
可用于其他使用场景。
patch()
作为函数装饰器,为你创建 mock 并将其传入被装饰的函数:
- >>> @patch('__main__.SomeClass')
- ... def function(normal_argument, mock_class):
- ... print(mock_class is SomeClass)
- ...
- >>> function(None)
- True
为类打补丁将把该类替换为 MagicMock
的 实例。 如果该类是在受测试的代码中被实例化的则它将为所要使用的 mock 的 return_value
。
如果该类被多次实例化则你可以使用 side_effect
来每次返回一个新 mock。 或者你也可以将 return_value 设为你希望的任何对象。
要在被打补丁的类的 实例 的方法上配置返回值你必须在 return_value
进行操作。 例如:
- >>> class Class:
- ... def method(self):
- ... pass
- ...
- >>> with patch('__main__.Class') as MockClass:
- ... instance = MockClass.return_value
- ... instance.method.return_value = 'foo'
- ... assert Class() is instance
- ... assert Class().method() == 'foo'
- ...
如果你使用 spec 或 spec_set 并且 patch()
替换的是 class,那么所创建的 mock 的返回值将具有同样的 spec。
- >>> Original = Class
- >>> patcher = patch('__main__.Class', spec=True)
- >>> MockClass = patcher.start()
- >>> instance = MockClass()
- >>> assert isinstance(instance, Original)
- >>> patcher.stop()
new_callable 参数适用于当你想要使用其他类来替代所创建的 mock 默认的 MagicMock
的场合。 例如,如果你想要使用 NonCallableMock
:
- >>> thing = object()
- >>> with patch('__main__.thing', new_callable=NonCallableMock) as mock_thing:
- ... assert thing is mock_thing
- ... thing()
- ...
- Traceback (most recent call last): ...
- TypeError: 'NonCallableMock' object is not callable
另一个使用场景是用 io.StringIO
实例来替换某个对象:
- >>> from io import StringIO
- >>> def foo():
- ... print('Something')
- ...
- >>> @patch('sys.stdout', new_callable=StringIO)
- ... def test(mock_stdout):
- ... foo()
- ... assert mock_stdout.getvalue() == 'Something\n'
- ...
- >>> test()
当 patch()
为你创建 mock 时,通常你需要做的第一件事就是配置该 mock。 某些配置可以在对 patch 的调用中完成。 你在调用时传入的任何关键字参数都将被用来在所创建的 mock 上设置属性:
- >>> patcher = patch('__main__.thing', first='one', second='two')
- >>> mock_thing = patcher.start()
- >>> mock_thing.first
- 'one'
- >>> mock_thing.second
- 'two'
除了所创建的 mock 的属性上的属性,例如 return_value
和 side_effect
,还可以配置子 mock 的属性。 将这些属性直接作为关键字参数传入在语义上是无效的,但是仍然能够使用 **
将以这些属性为键的字典扩展至一个 patch()
调用中
- >>> config = {'method.return_value': 3, 'other.side_effect': KeyError}
- >>> patcher = patch('__main__.thing', **config)
- >>> mock_thing = patcher.start()
- >>> mock_thing.method()
- 3
- >>> mock_thing.other()
- Traceback (most recent call last): ...
- KeyError
在默认情况下,尝试给某个模块中并不存在的函数(或者某个类中的方法或属性)打补丁将会失败并引发 AttributeError
:
- >>> @patch('sys.non_existing_attribute', 42)
- ... def test():
- ... assert sys.non_existing_attribute == 42
- ...
- >>> test()
- Traceback (most recent call last): ...
- AttributeError: <module 'sys' (builtin)> does not have the attribute 'non_existing_attribute'
但在对 patch()
的调用中添加 create=True
将使之前示例的效果符合预期:
- >>> @patch('sys.non_existing_attribute', 42, create=True)
- ... def test(mock_stdout):
- ... assert sys.non_existing_attribute == 42
- ...
- >>> test()
在 3.8 版本发生变更: 如果目标为异步函数那么 patch()
现在将返回一个 AsyncMock
。
patch.object
- patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)
- 用一个 mock 对象为对象 (target) 中指定名称的成员 (attribute) 打补丁。
patch.object()
可以被用作装饰器、类装饰器或上下文管理器。 new, spec, create, spec_set, autospec 和 new_callable 等参数的含义与 patch()
的相同。 与 patch()
类似,patch.object()
接受任意关键字参数用于配置它所创建的 mock 对象。
当用作类装饰器时 patch.object()
将认可 patch.TEST_PREFIX
作为选择所要包装方法的标准。
你可以附带三个参数或两个参数来调用 patch.object()
。 三个参数的形式将接受要打补丁的对象、属性的名称以及将要替换该属性的对象。
将附带两个参数的形式调用时你将省略替换对象,还会为你创建一个 mock 并作为附加参数传入被装饰的函数:
- >>> @patch.object(SomeClass, 'class_method')
- ... def test(mock_method):
- ... SomeClass.class_method(3)
- ... mock_method.assert_called_with(3)
- ...
- >>> test()
传给 patch.object()
的 spec, create 和其他参数的含义与 patch()
的同名参数相同。
patch.dict
- patch.dict(in_dict, values=(), clear=False, **kwargs)
- 为一个字典或字典类对象打补丁,并在测试之后将该目录恢复到其初始状态。
in_dict 可以是一个字典或映射类容器。 如果它是一个映射则它必须至少支持获取、设置和删除条目以及对键执行迭代。
in_dict 也可以是一个指定字典名称的字符串,然后将通过导入操作来获取该字典。
values 可以是一个要在字典中设置的值的字典。 values 也可以是一个包含 (key, value)
对的可迭代对象。
如果 clear 为真值则该字典将在设置新值之前先被清空。
patch.dict()
也可以附带任意关键字参数调用以设置字典中的值。
在 3.8 版本发生变更: 现在当 patch.dict()
被用作上下文管理器时将返回被打补丁的字典。now returns the patched dictionary when used as a context manager.
patch.dict()
可被用作上下文管理器、装饰器或类装饰器:
- >>> foo = {}
- >>> @patch.dict(foo, {'newkey': 'newvalue'})
- ... def test():
- ... assert foo == {'newkey': 'newvalue'}
- ...
- >>> test()
- >>> assert foo == {}
当被用作类装饰器时 patch.dict()
将认可 patch.TEST_PREFIX
(默认值为 'test'
) 作为选择所在包装方法的标准:
- >>> import os
- >>> import unittest
- >>> from unittest.mock import patch
- >>> @patch.dict('os.environ', {'newkey': 'newvalue'})
- ... class TestSample(unittest.TestCase):
- ... def test_sample(self):
- ... self.assertEqual(os.environ['newkey'], 'newvalue')
如果你在为你的测试使用不同的前缀,你可以通过设置 patch.TEST_PREFIX
来将不同的前缀告知打补丁方。 有关如何修改该值的详情请参阅 TEST_PREFIX。
patch.dict()
可被用来向一个字典添加成员,或者简单地让测试修改一个字典,并确保当测试结束时恢复该字典。
- >>> foo = {}
- >>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
- ... assert foo == {'newkey': 'newvalue'}
- ... assert patched_foo == {'newkey': 'newvalue'}
- ... # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
- ... patched_foo['spam'] = 'eggs'
- ...
- >>> assert foo == {}
- >>> assert patched_foo == {}
- >>> import os
- >>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
- ... print(os.environ['newkey'])
- ...
- newvalue
- >>> assert 'newkey' not in os.environ
可以在 patch.dict()
调用中使用关键字来设置字典的值:
- >>> mymodule = MagicMock()
- >>> mymodule.function.return_value = 'fish'
- >>> with patch.dict('sys.modules', mymodule=mymodule):
- ... import mymodule
- ... mymodule.function('some', 'args')
- ...
- 'fish'
patch.dict()
可被用于实际上不是字典的字典类对象。 它们至少必须支持条目获取、设置、删除以及迭代或成员检测两者之一。 这对应于魔术方法 __getitem__()
, __setitem__()
, __delitem__()
以及 __iter__()
或 __contains__()
两者之一。
- >>> class Container:
- ... def __init__(self):
- ... self.values = {}
- ... def __getitem__(self, name):
- ... return self.values[name]
- ... def __setitem__(self, name, value):
- ... self.values[name] = value
- ... def __delitem__(self, name):
- ... del self.values[name]
- ... def __iter__(self):
- ... return iter(self.values)
- ...
- >>> thing = Container()
- >>> thing['one'] = 1
- >>> with patch.dict(thing, one=2, two=3):
- ... assert thing['one'] == 2
- ... assert thing['two'] == 3
- ...
- >>> assert thing['one'] == 1
- >>> assert list(thing) == ['one']
patch.multiple
- patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)
- 在单个调用中执行多重补丁。 它接受要打补丁的对象(一个对象或一个通过导入来获取对象的字符串)以及用于补丁的关键字参数:
- with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'):
- ...
如果你希望 patch.multiple()
为你创建 mock 则要使用 DEFAULT
作为值。 在此情况下所创建的 mock 会通过关键字参数传入被装饰的函数,而当 patch.multiple()
被用作上下文管理器时则将返回一个字典。
patch.multiple()
可以被用作装饰器、类装饰器或上下文管理器。 spec, spec_set, create, 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()
可以与其他 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
所有 patcher 对象都具有 start()
和 stop()
方法。 使用这些方法可以更简单地在 setUp
方法上打补丁,还可以让你不必嵌套使用装饰器或 with 语句就能打多重补丁。
要使用这些方法请按正常方式调用 patch()
, patch.object()
或 patch.dict()
并保留一个指向所返回 patcher
对象的引用。 你可以随后调用 start()
来原地打补丁并调用 stop()
来恢复它。
如果你使用 patch()
来创建自己的 mock 那么将可通过调用 patcher.start
来返回它。
- >>> patcher = patch('package.module.ClassName')
- >>> from package import module
- >>> original = module.ClassName
- >>> new_mock = patcher.start()
- >>> assert module.ClassName is not original
- >>> assert module.ClassName is new_mock
- >>> patcher.stop()
- >>> assert module.ClassName is original
- >>> assert module.ClassName is not new_mock
此操作的一个典型应用场景是在一个 TestCase
的 setUp
方法中执行多重补丁:
- >>> class MyTest(unittest.TestCase):
- ... def setUp(self):
- ... self.patcher1 = patch('package.module.Class1')
- ... self.patcher2 = patch('package.module.Class2')
- ... self.MockClass1 = self.patcher1.start()
- ... self.MockClass2 = self.patcher2.start()
- ...
- ... def tearDown(self):
- ... self.patcher1.stop()
- ... self.patcher2.stop()
- ...
- ... def test_something(self):
- ... assert package.module.Class1 is self.MockClass1
- ... assert package.module.Class2 is self.MockClass2
- ...
- >>> MyTest('test_something').run()
小心
如果你要使用这个技巧则你必须通过调用 stop
来确保补丁被“恢复”。 这可能要比你想像的更麻烦,因为如果在 setUp
中引发了异常那么 tearDown
将不会被调用。 unittest.TestCase.addCleanup()
可以简化此操作:
- >>> class MyTest(unittest.TestCase):
- ... def setUp(self):
- ... patcher = patch('package.module.Class')
- ... self.MockClass = patcher.start()
- ... self.addCleanup(patcher.stop)
- ...
- ... def test_something(self):
- ... assert package.module.Class is self.MockClass
- ...
一项额外的好处是你不再需要保留指向 patcher
对象的引用。
还可以通过使用 patch.stopall()
来停止已启动的所有补丁。
- patch.stopall()
- 停止所有激活的补丁。 仅会停止通过
start
启动的补丁。
为内置函数打补丁
你可以为一个模块中的任何内置函数打补丁。 以以示例是为内置函数 ord()
打补丁:
- >>> @patch('__main__.ord')
- ... def test(mock_ord):
- ... mock_ord.return_value = 101
- ... print(ord('c'))
- ...
- >>> test()
- 101
TEST_PREFIX
所有补丁都可被用作类装饰器。 当以这种方式使用时它们将会包装类中的每个测试方法。 补丁会将以名字以 'test'
开头的方法识别为测试方法。 这与 unittest.TestLoader
查找测试方法的默认方式相同。
你可能会想要为你的测试使用不同的前缀。 你可以通过设置 patch.TEST_PREFIX
来告知打补丁方不同的前缀:
- >>> patch.TEST_PREFIX = 'foo'
- >>> value = 3
- >>>
- >>> @patch('__main__.value', 'not three')
- ... class Thing:
- ... def foo_one(self):
- ... print(value)
- ... def foo_two(self):
- ... print(value)
- ...
- >>>
- >>> Thing().foo_one()
- not three
- >>> Thing().foo_two()
- not three
- >>> value
- 3
嵌套补丁装饰器
如果你想要应用多重补丁那么你可以简单地堆叠多个装饰器。
你可以使用以下模式来堆叠多个补丁装饰器:
- >>> @patch.object(SomeClass, 'class_method')
- ... @patch.object(SomeClass, 'static_method')
- ... def test(mock1, mock2):
- ... assert SomeClass.static_method is mock1
- ... assert SomeClass.class_method is mock2
- ... SomeClass.static_method('foo')
- ... SomeClass.class_method('bar')
- ... return mock1, mock2
- ...
- >>> mock1, mock2 = test()
- >>> mock1.assert_called_once_with('foo')
- >>> mock2.assert_called_once_with('bar')
请注意装饰器是从下往上被应用的。 这是 Python 应用装饰器的标准方式。 被创建并传入你的测试函数的 mock 的顺序也将匹配这个顺序。