Skip to main content

26.5. unittest.mock — mock对象库

3.3 新版功能.

源代码: Lib/unittest/mock.py


unittest.mock 是一个用于在Python中测试的库。它允许您使用模拟对象替换测试中的系统部件,并对其使用方式进行断言。

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

另外,mock提供了一个 patch() 装饰器,用于处理测试范围内的修补模块和类级别属性,以及用于创建唯一对象的 sentinel。有关如何使用 MockMagicMockpatch() 的一些示例,请参阅 quick guide

Mock非常容易使用,并且设计用于 unittest。 Mock基于“action - > assertion”模式,而不是许多模拟框架使用的“record - > replay”。

对于早期版本的Python,有一个 unittest.mock 的反向端口,可以作为 模拟PyPI

26.5.1. 快速指南

MockMagicMock 对象在访问它们时创建所有属性和方法,并存储它们的使用细节。您可以配置它们,指定返回值或限制可用的属性,然后对其使用方式进行断言:

>>> from unittest.mock import MagicMock
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')

side_effect 允许您执行副作用,包括在调用模拟时引发异常:

>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
 ...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
...     return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)

Mock有很多其他方法可以配置它和控制它的行为。例如,spec 参数配置模拟从另一个对象获取其规范。尝试访问规范中不存在的模拟上的属性或方法将失败,并显示 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() 重要的是,您可以在命名空间中对其进行修补的对象。这通常是直截了当的,但是快速指南阅读 在哪里补丁

除了装饰器 patch() 可以用作语句中的上下文管理器:

>>> 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 类只是一个Mock变体,具有为您预先创建的所有魔法方法(以及所有有用的方法)。

以下是使用魔术方法与普通Mock类的示例:

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

为了确保测试中的模拟对象具有与它们替换的对象相同的API,您可以使用 自动投标。自动指定可以通过 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. 模拟类

Mock 是一个灵活的模拟对象,旨在取代在代码中使用存根和测试双打。当您访问 [1] 时,Mock是可调用的并将属性创建为新的嘲笑。访问相同的属性将总是返回相同的模拟。 Mocks记录你如何使用它们,让你对你的代码做了什么断言。

MagicMockMock 的一个子类,所有的魔法方法都是预先创建的并且可以使用。还有不可调用的变体,当你模拟不可调用的对象时很有用:NonCallableMockNonCallableMagicMock

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_setspec 的更严格的变体。如果使用,尝试 set 或获得一个属性,不是在作为 spec_set 传递的对象上的模拟将提出 AttributeError

  • side_effect:每当调用Mock时调用的函数。请参阅 side_effect 属性。用于提高异常或动态更改返回值。该函数使用与mock相同的参数调用,除非返回 DEFAULT,否则将使用此函数的返回值作为返回值。

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

    如果 side_effect 是一个可迭代的,那么对模拟的每次调用都将返回可迭代的下一个值。

    side_effect 可以通过将其设置为 None 来清除。

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

  • unsafe:默认情况下,如果任何属性以 assertassret 开头将产生 AttributeError。传递 unsafe=True 将允许访问这些属性。

    3.5 新版功能.

  • wraps:要包装的模拟对象的项目。如果 wraps 不是 None,那么调用Mock会将调用传递给包装的对象(返回真实结果)。对模拟的属性访问将返回一个Mock对象,该对象包装被包装对象的相应属性(因此试图访问一个不存在的属性将产生一个 AttributeError)。

    如果模拟具有显式 return_value 集合,那么调用不会传递到包装的对象,而是返回 return_value

  • name:如果模拟有一个名称,那么它将被用于模拟的repr。这可以用于调试。该名称将传播到子模型。

Mocks也可以使用任意关键字参数调用。这些将用于在创建模拟之后设置属性。有关详细信息,请参阅 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 = 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)

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

如果模拟已经调用了 ever,那么断言通过,与 assert_called_with()assert_called_once_with() 不同,如果调用是最近的调用,则AATE和 assert_called_once_with() 仅通过。

>>> mock = Mock(return_value=None)
>>> mock(1, 2, arg='thing')
>>> mock('some', 'thing', 'else')
>>> mock.assert_any_call(1, 2, arg='thing')
assert_has_calls(calls, any_order=False)

断言模拟已经使用指定的调用进行调用。将检查 mock_calls 列表中的呼叫。

如果 any_order 为false(默认值),则调用必须是顺序的。在指定的调用之前或之后可以有额外的调用。

如果 any_order 为真,则呼叫可以是任何顺序,但它们必须全部出现在 mock_calls 中。

>>> mock = Mock(return_value=None)
>>> mock(1)
>>> mock(2)
>>> mock(3)
>>> mock(4)
>>> calls = [call(2), call(3)]
>>> mock.assert_has_calls(calls)
>>> calls = [call(4), call(2), call(3)]
>>> mock.assert_has_calls(calls, any_order=True)
assert_not_called()

断言模拟从未被调用。

>>> m = Mock()
>>> m.hello.assert_not_called()
>>> obj = m.hello()
>>> m.hello.assert_not_called()
Traceback (most recent call last):
  ...
AssertionError: Expected 'hello' to not have been called. Called 1 times.

3.5 新版功能.

reset_mock(*, return_value=False, side_effect=False)

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() doesn’t 会清除返回值,side_effect 或您在默认情况下使用正常分配设置的任何子属性。如果要重置 return_valueside_effect,则将相应的参数作为 True 传递。子模块和返回值模拟(如果有)也被重置。

注解

return_valueside_effect 是仅关键字参数。

mock_add_spec(spec, spec_set=False)

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

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

attach_mock(mock, attribute)

将mock作为此属性的一个属性,替换其名称和父级。对附加模拟的调用将记录在此 method_callsmock_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_listmethod_callsmock_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 == expected
True

call_args_list 的成员是 call 对象。这些可以解包为元组来获得单个参数。参见 调用作为元组

method_calls

除了跟踪对自身的调用,mock还跟踪对方法和属性的调用,以及 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 调用mock对象,其方法,魔法方法 and 返回值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 对象。这些可以解包为元组来获得单个参数。参见 调用作为元组

__class__

通常,对象的 __class__ 属性将返回其类型。对于具有 spec 的模拟对象,__class__ 会返回spec类。这允许模拟对象通过 isinstance() 测试为他们正在替换/伪装为:

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

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

>>> mock = Mock()
>>> mock.__class__ = dict
>>> isinstance(mock, dict)
True
class unittest.mock.NonCallableMock(spec=None, wraps=None, name=None, spec_set=None, **kwargs)

Mock 的不可调用版本。构造函数参数具有与 Mock 相同的含义,除了 return_valueside_effect 之外,它们对不可调用的模拟没有意义。

使用类或实例作为 specspec_set 的Mock对象能够通过 isinstance() 测试:

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

Mock 类支持模拟魔术方法。有关详细信息,请参阅 魔法

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

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

子模块的返回值和副作用可以使用点符号以相同的方式设置。因为你不能在调用中直接使用点名,你必须创建一个字典并使用 ** 解压缩它:

>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
  ...
KeyError

使用 spec (或 spec_set)创建的可调用模拟在匹配调用模拟时将会自动检查规范对象的签名。因此,它可以匹配实际调用的参数,而不管它们是通过位置还是名称:

>>> def f(a, b, c): pass
...
>>> mock = Mock(spec=f)
>>> mock(1, 2, c=3)
<Mock name='mock()' id='140161580456576'>
>>> mock.assert_called_with(1, 2, 3)
>>> mock.assert_called_with(a=1, b=2, c=3)

这适用于 assert_called_with()assert_called_once_with()assert_has_calls()assert_any_call()。当 自动指定 时,它也将应用于对模拟对象的方法调用。

在 3.4 版更改: 对specced和autospecced模拟对象添加签名自省。

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. 调用

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

对对象的调用将记录在属性(如 call_argscall_args_list)中。

如果设置了 side_effect,则在记录呼叫之后将调用 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 函数。这允许您基于输入动态地改变调用的返回值:

>>> 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 也可以是任何可迭代对象。对模拟器的重复调用将从迭代器返回值(直到迭代器耗尽并产生 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. 删除属性

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. 模拟名称和名称属性

因为“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. 附加模型作为属性

当你将一个模拟作为另一个模拟的属性(或作为返回值),它成为那个模拟的“孩子”。对子代的调用记录在父代的 method_callsmock_calls 属性中。这对于配置子模板,然后将它们附加到父模型,或者将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)]

例外情况是,如果模拟有一个名称。这允许你防止“parenting”如果由于某种原因,你不想它发生。

>>> 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对象的 very 混乱。如果你需要魔法方法支持,请参阅 魔法

26.5.3. 补丁

补丁装饰器仅用于在它们装饰的函数的范围内修补对象。即使出现异常,它们也会自动处理解压缩。所有这些函数也可以用于语句或类装饰器。

26.5.3.1. 补丁

注解

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语句的正文中,targetnew 对象修补。当函数/with语句退出时,补丁被取消。

如果省略 new,则用 MagicMock 替换目标。如果 patch() 用作装饰器并且 new 被省略,则创建的模拟作为附加参数被传递到装饰函数。如果 patch() 用作上下文管理器,则上下文管理器返回所创建的模拟。

target 应该是 'package.module.ClassName' 形式的字符串。 target 被导入并且指定的对象被 new 对象替换,因此 target 必须能够从调用 patch() 的环境中导入。当执行装饰功能时,不是在装饰时导入目标。

如果修补程序为您创建一个,specspec_set 关键字参数将传递给 MagicMock

此外,您可以传递 spec=Truespec_set=True,这导致补丁在作为spec/spec_set对象被嘲笑的对象中传递。

new_callable 允许您指定一个不同的类或可调用对象,将被调用以创建 new 对象。默认使用 MagicMock

spec 的一个更强大的形式是 autospec。如果设置 autospec=True,那么将使用要替换的对象的spec来创建模拟。模拟的所有属性也将具有被替换的对象的相应属性的spec。被模拟的方法和函数将检查它们的参数,并且如果它们以错误的签名被调用,则将产生 TypeError。对于替换类的mock,它们的返回值(’instance’)将具有与该类相同的规格。参见 create_autospec() 功能和 自动指定

而不是 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 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'
...

如果使用 specspec_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() 为你创建一个模拟时,通常你需要做的第一件事是配置模拟。一些配置可以在调用patch中完成。任何传入调用的任意关键字都将用于设置所创建模拟的属性:

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

除了所创建的模拟属性(例如 return_valueside_effect)上的属性之外,还可以配置子模型的属性。这些在句法上不能直接作为关键字参数传递,但是具有这些键的字典仍然可以使用 ** 扩展为 patch() 调用:

>>> config = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> patcher = patch('__main__.thing', **config)
>>> mock_thing = patcher.start()
>>> mock_thing.method()
3
>>> mock_thing.other()
Traceback (most recent call last):
  ...
KeyError

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)

使用模拟对象在对象(target)上修补命名成员(attribute)。

patch.object() 可以用作装饰器,类装饰器或上下文管理器。参数 newspeccreatespec_setautospecnew_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()

speccreatepatch.object() 的其他参数具有与 patch() 相同的含义。

26.5.3.3. patch.dict

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

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

in_dict 可以是字典或像容器的映射。如果它是一个映射,那么它必须至少支持获取,设置和删除项目以及迭代键。

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

values 可以是在字典中设置的值的字典。 values 也可以是 (key, value) 对的可迭代。

如果 clear 为真,则在设置新值之前字典将被清除。

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() 可以用于字典,像实际上不是字典的对象。至少他们必须支持项目获取,设置,删除和迭代或成员资格测试。这对应于神奇的方法 __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']

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() 可以用作装饰器,类装饰器或上下文管理器。参数 specspec_setcreateautospecnew_callable 具有与 patch() 相同的含义。这些参数将应用于 patch.multiple() 完成的 all 修补程序。

当用作类装饰器时,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 装饰器,但是通过关键字 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() 用作上下文管理器,则上下文管理器返回的值是一个字典,其中创建的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 方法中进行修补更简单,或者您希望在没有嵌套修饰符或语句的情况下执行多个修补程序。

要使用它们,正常调用 patch()patch.object()patch.dict(),并保留对返回的 patcher 对象的引用。然后,您可以调用 start() 将补丁放入位置,stop() 将其撤消。

如果你使用 patch() 为你创建一个模拟,那么它将被调用返回给 patcher.start

>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock

这种情况的典型用例可能是在 TestCasesetUp 方法中执行多个修补程序:

>>> 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. 嵌套补丁装饰

如果你想要执行多个补丁,那么你可以简单地堆叠装饰。

您可以使用此模式堆叠多个修补程序装饰:

>>> @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. 在哪里打补丁

patch() 通过(暂时)改变 name 指向的对象与另一个对象。可以有许多名称指向任何单独的对象,因此为了修补工作,您必须确保修补被测试系统使用的名称。

基本原则是,您修补对象是 抬起头 的位置,它不一定与定义位置相同。几个例子将有助于澄清这一点。

想象一下,我们有一个项目,我们想用下面的结构来测试:

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 在哪里使用(或在哪里查找)。在这种情况下,some_function 实际上在模块b中查找 SomeClass,我们已经导入了它。修补应该看起来像:

@patch('b.SomeClass')

然而,考虑替代方案,其中代替 from a import SomeClass 模块b是 import a,而 some_function 使用 a.SomeClass。这两种导入形式是常见的。在这种情况下,我们想要修补的类是在模块中查找,因此我们必须修补 a.SomeClass:

@patch('a.SomeClass')

26.5.3.10. 修补描述符和代理对象

patchpatch.object 都正确地修补和恢复描述符:类方法,静态方法和属性。你应该修补这些在 class 而不是一个实例。他们还使用代理属性访问的 some 对象,如 django设置对象

26.5.4. MagicMock和魔法方法支持

26.5.4.1. 嘲笑魔法

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

因为魔法方法不同于正常方法 [2],这种支持已经被专门实现。这意味着只支持特定的魔法方法。支持的列表包括所有的 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 中。

注解

如果使用 spec 关键字参数创建一个模拟,那么试图设置一个不在规范中的魔术方法将会产生一个 AttributeError

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

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

26.5.4.2. 魔术模拟

有两种 MagicMock 变体:MagicMockNonCallableMagicMock

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

MagicMockMock 的子类,默认实现大多数魔术方法。你可以使用 MagicMock,而不必自己配置魔术方法。

构造函数参数具有与 Mock 相同的含义。

如果使用 specspec_set 参数,那么将创建规范中存在的 only 魔术方法。

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

MagicMock 的不可调用版本。

构造函数参数具有与 MagicMock 相同的含义,除了 return_valueside_effect 之外,它们对不可调用的模拟没有意义。

魔法方法使用 MagicMock 对象进行设置,因此您可以配置它们并以通常的方式使用它们:

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

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

方法及其默认值:

  • __lt__:未实现

  • __gt__:未实现

  • __le__:未实现

  • __ge__:未实现

  • __int__:1

  • __contains__:假

  • __len__:0

  • __iter__:iter([])

  • __exit__:假

  • __complex__:1j

  • __float__:1.0

  • __bool__:真的

  • __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']

如果返回值 is 是一个迭代器,那么迭代它一次会消耗它,随后的迭代将产生一个空列表:

>>> 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对应用此规则不一致。支持的协议方法应该与所有受支持的Python版本配合使用。

[3]

该函数基本上连接到类,但每个 Mock 实例保持与其他实例隔离。

26.5.5. 帮助

26.5.5.1. 哨兵

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. 默认

unittest.mock.DEFAULT

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

26.5.5.3. 呼叫

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

call() 是一个帮助对象,用于进行更简单的断言,用于与 call_argscall_args_listmock_callsmethod_calls 进行比较。 call() 也可以与 assert_has_calls() 一起使用。

>>> m = MagicMock(return_value=None)
>>> m(1, 2, a='foo', b='bar')
>>> m()
>>> m.call_args_list == [call(1, 2, a='foo', b='bar'), call()]
True
call.call_list()

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

call_list 特别适用于对“链接调用”进行断言。链接调用是单行代码上的多个调用。这导致 mock_calls 中的多个条目在模拟。手动构造调用序列可能是乏味的。

call_list() 可以构造来自相同链接调用的调用序列:

>>> m = MagicMock()
>>> m(1).method(arg='foo').other('bar')(2.0)
<MagicMock name='mock().method().other()()' id='...'>
>>> kall = call(1).method(arg='foo').other('bar')(2.0)
>>> kall.call_list()
[call(1),
 call().method(arg='foo'),
 call().method().other('bar'),
 call().method().other()(2.0)]
>>> m.mock_calls == kall.call_list()
True

call 对象是(位置args,关键字args)或(名称,位置args,关键字args)的元组,取决于它是如何构造的。当你自己构建它们时,这不是特别有趣,但是可以内省在 Mock.call_argsMock.call_args_listMock.mock_calls 属性中的 call 对象以获得它们包含的单个参数。

Mock.call_argsMock.call_args_list 中的 call 对象是(位置args,关键字args)的两元组,而 Mock.mock_calls 中的 call 对象以及您自己构建的对象是(名称,位置args,关键字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_setTrue,那么尝试设置spec对象上不存在的属性将产生 AttributeError

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

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

有关如何使用 create_autospec() 的自动指定和 patch()autospec 参数的示例,请参阅 自动指定

26.5.5.5. 任何

unittest.mock.ANY

有时你可能需要在调用mock时对 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

26.5.5.6. 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 是私有的,而不是被嘲笑的东西)下划线和双下划线前缀属性已经从 Mock 上调用 dir() 的结果中过滤出来。如果您不喜欢此行为,您可以通过设置模块级开关 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)

一个帮助函数来创建一个模拟来替换 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 的调用时复位。

使用 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_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 已经提供了一个功能来帮助这个,称为投标。如果你使用类或实例作为模拟的 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 参数,那么要替换的对象将用作规范对象。因为指定是“懒惰”(规范是作为模拟上的属性创建的),所以你可以使用它与非常复杂或深层嵌套的对象(如导入模块导入模块的模块),而没有大的性能命中。

下面是一个使用示例:

>>> 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 有一个spec。 request.Request 在构造函数中使用两个参数(其中之一是 self)。这里是如果我们试图调用不正确会发生什么:

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

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

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

Request 对象是不可调用的,所以实例化我们的模拟 request.Request 的返回值是一个不可调用的模拟。使用规范,我们的声明中的任何打字都会引发正确的错误:

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

在许多情况下,您只能将 autospec=True 添加到您现有的 patch() 呼叫中,然后防止由于拼写错误和api更改而导致的错误。

除了通过 patch() 使用 autospec,还有一个 create_autospec() 用于直接创建自动指定的mock:

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

这不是没有注意和限制,但这是为什么它不是默认行为。为了知道什么属性可用的规范对象,autospec必须introspect(访问属性)spec。当你遍历模型上的属性时,相应的原始对象的遍历正在发生。如果任何您指定的对象具有可触发代码执行的属性或描述符,那么您可能无法使用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
...

有一个更积极的版本的 specautospecdoes 阻止您设置不存在的属性。这是有用的,如果你想确保你的代码只有 sets 有效的属性,但显然它防止这种特殊的情况:

>>> with patch('__main__.Something', autospec=True, spec_set=True):
...   thing = Something()
...   thing.a = 33
...
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'a'

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

class Something:
    a = 33

这带来了另一个问题。对于稍后将是不同类型的对象的成员提供默认值 None 是相对常见的。 None 将作为规范是无用的,因为它不会让你访问 any 属性或方法。由于 Nonenever 作为规范有用,并且可能表示通常为某种其他类型的成员,autospec不会为设置为 None 的成员使用规范。这些只是普通的模拟(好 - 魔术):

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

如果修改你的生产类添加默认值不是你的喜好,那么有更多的选择。其中之一是简单地使用一个实例作为spec而不是类。另一种是创建生产类的子类,并将默认值添加到子类而不影响生产类。这两个都需要使用替代对象作为规范。谢谢 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() 的调用 - 完成。