unittest.mock --- 模擬對象庫?

3.3 新版功能.

源代碼: Lib/unittest/mock.py


unittest.mock 是一個(gè)用于測試的Python庫。它允許使用模擬對象來(lái)替換受測系統的一些部分,并對這些部分如何被使用進(jìn)行斷言判斷。

unittest.mock 提供的 Mock 類(lèi),能在整個(gè)測試套件中模擬大量的方法。創(chuàng )建后,就可以斷言調用了哪些方法/屬性及其參數。還可以以常規方式指定返回值并設置所需的屬性。

此外,mock 還提供了用于修補測試范圍內模塊和類(lèi)級別屬性的 patch() 裝飾器,和用于創(chuàng )建獨特對象的 sentinel 。 閱讀 quick guide 中的案例了解如何使用 Mock ,MagicMockpatch() 。

mock 被設計為配合 unittest 使用且它是基于 'action -> assertion' 模式而非許多模擬框架所使用的 'record -> replay' 模式。

在 Python 的早期版本要單獨使用 unittest.mock ,在 PyPI 獲取 mock 。

快速上手?

當您訪(fǎng)問(wèn)對象時(shí), MockMagicMock 將創(chuàng )建所有屬性和方法,并保存他們在使用時(shí)的細節。你可以通過(guò)配置,指定返回值或者限制可訪(fǎng)問(wèn)屬性,然后斷言他們如何被調用:

>>>
>>> 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')

通過(guò) side_effect 設置副作用(side effects) ,可以是一個(gè) 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 還可以通過(guò)其他方法配置和控制其行為。例如 mock 可以通過(guò)設置 spec 參數來(lái)從一個(gè)對象中獲取其規格(specification)。如果訪(fǎng)問(wèn) mock 的屬性或方法不在 spec 中,會(huì )報 AttributeError 錯誤。

使用 patch() 裝飾去/上下文管理器,可以更方便地測試一個(gè)模塊下的類(lèi)或對象。你指定的對象會(huì )在測試過(guò)程中替換成 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 裝飾器時(shí),mock 將以執行順序傳遞給裝飾器函數(Python 裝飾器正常順序)。由于從下至上,因此在上面的示例中,首先 mock 傳入的 module.ClassName1 。

在查找對象的名稱(chēng)空間中修補對象使用 patch() 。使用起來(lái)很簡(jiǎn)單,閱讀 在哪里打補丁 來(lái)快速上手。

patch() 也可以 with 語(yǔ)句中使用上下文管理。

>>>
>>> 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)

還有一個(gè) patch.dict() 用于在一定范圍內設置字典中的值,并在測試結束時(shí)將字典恢復為其原始狀態(tài):

>>>
>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
...     assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original

Mock支持 Python 魔術(shù)方法 。使用模式方法最簡(jiǎn)單的方式是使用 MagicMock class. 。它可以做如下事情:

>>>
>>> mock = MagicMock()
>>> mock.__str__.return_value = 'foobarbaz'
>>> str(mock)
'foobarbaz'
>>> mock.__str__.assert_called_with()

Mock 能指定函數(或其他 Mock 實(shí)例)為魔術(shù)方法,它們將被適當地調用。 MagicMock 是預先創(chuàng )建了所有魔術(shù)方法(所有有用的方法) 的 Mock 。

下面是一個(gè)使用了普通 Mock 類(lèi)的魔術(shù)方法的例子

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

使用 auto-speccing 可以保證測試中的模擬對象與要替換的對象具有相同的api 。在 patch 中可以通過(guò) autospec 參數實(shí)現自動(dòng)推斷,或者使用 create_autospec() 函數。自動(dòng)推斷會(huì )創(chuàng )建一個(gè)與要替換對象相同的屬相和方法的模擬對象,并且任何函數和方法(包括構造函數)都具有與真實(shí)對象相同的調用簽名。

這么做是為了因確保不當地使用 mock 導致與生產(chǎn)代碼相同的失?。?/p>

>>>
>>> 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)

在類(lèi)中使用 create_autospec() 時(shí),會(huì )復制 __init__ 方法的簽名,另外在可調用對象上使用時(shí),會(huì )復制 __call__ 方法的簽名。

Mock 類(lèi)?

Mock 是一個(gè)可以靈活的替換存根 (stubs) 的對象,可以測試所有代碼。 Mock 是可調用的,在訪(fǎng)問(wèn)其屬性時(shí)創(chuàng )建一個(gè)新的 mock 1 。訪(fǎng)問(wèn)相同的屬性只會(huì )返回相同的 mock 。 Mock 會(huì )保存調用記錄,可以通過(guò)斷言獲悉代碼的調用。

MagicMockMock 的子類(lèi),它有所有預創(chuàng )建且可使用的魔術(shù)方法。在需要模擬不可調用對象時(shí),可以使用 NonCallableMock  和 NonCallableMagicMock

patch() 裝飾器使得用 Mock 對象臨時(shí)替換特定模塊中的類(lèi)非常方便。 默認情況下 patch() 將為你創(chuàng )建一個(gè) MagicMock。 你可以使用 patch()new_callable 參數指定替代 Mock 的類(lèi)。

class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)?

創(chuàng )建一個(gè)新的 Mock 對象。通過(guò)可選參數指定 Mock 對象的行為:

  • spec: 可以是要給字符串列表,也可以是充當模擬對象規范的現有對象(類(lèi)或實(shí)例)。如果傳入一個(gè)對象,則通過(guò)在該對象上調用 dir 來(lái)生成字符串列表(不支持的魔法屬性和方法除外)。訪(fǎng)問(wèn)不在此列表中的任何屬性都將觸發(fā) AttributeError 。

    如果 spec 是一個(gè)對象(而不是字符串列表),則 __class__ 返回 spec 對象的類(lèi)。 這允許模擬程序通過(guò) isinstance() 測試。

  • spec_setspec 的更嚴格的變體。如果使用了該屬性,嘗試模擬 setget 的屬性不在 spec_set 所包含的對象中時(shí),會(huì )拋出 AttributeError 。

  • side_effect :每當調用 Mock 時(shí)都會(huì )調用的函數。 參見(jiàn) side_effect 屬性。 對于引發(fā)異?;騽?dòng)態(tài)更改返回值很有用。 該函數使用與 mock 函數相同的參數調用,并且除非返回 DEFAULT ,否則該函數的返回值將用作返回值。

    另外, side_effect 可以是異常類(lèi)或實(shí)例。 此時(shí),調用模擬程序時(shí)將引發(fā)異常。

    如果 side_effect 是可迭代對象,則每次調用 mock 都將返回可迭代對象的下一個(gè)值。

    設置 side_effectNone 即可清空。

  • return_value :調用 mock 的返回值。 默認情況下,是一個(gè)新的Mock(在首次訪(fǎng)問(wèn)時(shí)創(chuàng )建)。 參見(jiàn) return_value 屬性 。

  • unsafe: By default, accessing any attribute whose name starts with assert, assret, asert, aseert or assrt will raise an AttributeError. Passing unsafe=True will allow access to these attributes.

    3.5 新版功能.

  • wraps :要包裝的 mock 對象。 如果 wraps 不是 None ,那么調用 Mock 會(huì )將調用傳遞給 wraps 的對象(返回實(shí)際結果)。 對模擬的屬性訪(fǎng)問(wèn)將返回一個(gè) Mock 對象,該對象包裝了 wraps 對象的相應屬性(因此,嘗試訪(fǎng)問(wèn)不存在的屬性將引發(fā) AttributeError )。

    如果明確指定 return_value ,則調用時(shí)不會(huì )返回包裝對象,而是返回 return_value 。

  • name :mock 的名稱(chēng)。 在調試時(shí)很有用。 名稱(chēng)會(huì )傳遞到子 mock 。

還可以使用任意關(guān)鍵字參數來(lái)調用 mock 。 創(chuàng )建模擬后,將使用這些屬性來(lái)設置 mock 的屬性。 有關(guān)詳細信息,請參見(jiàn) configure_mock() 方法。

assert_called()?

斷言 mock 已被調用至少一次。

>>>
>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called()

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.

3.6 新版功能.

assert_called_with(*args, **kwargs)?

此方法是斷言上次調用已以特定方式進(jìn)行的一種便捷方法:

>>>
>>> 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.
assert_any_call(*args, **kwargs)?

斷言 mock 已被調用并附帶了指定的參數。

如果 mock 曾經(jīng) 被調用過(guò)則斷言通過(guò),不同于 assert_called_with()assert_called_once_with() 那樣只有在調用是最近的一次時(shí)才會(huì )通過(guò),而對于 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 為假值則調用必須是順序進(jìn)行的。 在指定的調用之前或之后還可以有額外的調用。

如果 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 從未被調用過(guò)。

>>>
>>> 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 函數添加了兩個(gè)僅限關(guān)鍵字參數。

這在你想要創(chuàng )建一系列重用相同對象的斷言時(shí)會(huì )很有用處。 請注意 reset_mock() 不會(huì ) 清除返回值、side_effect 或任何你使用普通賦值所默認設置的子屬性。 如果你想要重置 return_valueside_effect,則要為相應的形參傳入 True。 子 mock 和返回值 mock (如果有的話(huà)) 也會(huì )被重置。

備注

return_valueside_effect 均為僅限關(guān)鍵字參數。

mock_add_spec(spec, spec_set=False)?

為 mock 添加描述。 spec 可以是一個(gè)對象或字符串列表。 只有 spec 上的屬性可以作為來(lái)自 mock 的屬性被獲取。

如果 spec_set 為真值則只有 spec 上的屬性可以被設置。

attach_mock(mock, attribute)?

附加一個(gè) mock 作為這一個(gè)的屬性,替換它的名稱(chēng)和上級。 對附加 mock 的調用將記錄在這一個(gè)的 method_callsmock_calls 屬性中。

configure_mock(**kwargs)?

通過(guò)關(guān)鍵字參數在 mock 上設置屬性。

屬性加返回值和附帶影響可以使用標準點(diǎn)號標記在子 mock 上設置并在方法調用中解包一個(gè)字典:

>>>
>>> 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 被創(chuàng )建之后的配置更為容易。

__dir__()?

Mock 對象會(huì )將 dir(some_mock) 的結果限制為有用結果。 對于帶有 spec 的 mock 這還包括 mock 的所有被允許的屬性。

請查看 FILTER_DIR 了解此過(guò)濾做了什么,以及如何停用它。

_get_child_mock(**kw)?

創(chuàng )建針對屬性和返回值的子 mock。 默認情況下子 mock 將為與其上級相同的類(lèi)型。 Mock 的子類(lèi)可能需要重載它來(lái)定制子 mock 的創(chuàng )建方式。

對于非可調用的 mock 將會(huì )使用可調用的變化形式(而非不是任意的自定義子類(lèi))。

called?

一個(gè)表示 mock 對象是否已被調用的布爾值:

>>>
>>> mock = Mock(return_value=None)
>>> mock.called
False
>>> mock()
>>> mock.called
True
call_count?

一個(gè)告訴你 mock 對象已被調用多少次的整數值:

>>>
>>> mock = Mock(return_value=None)
>>> mock.call_count
0
>>> mock()
>>> mock()
>>> mock.call_count
2
return_value?

設置這個(gè)來(lái)配置通過(guò)調用該 mock 所返回的值:

>>>
>>> mock = Mock()
>>> mock.return_value = 'fish'
>>> mock()
'fish'

默認的返回值是一個(gè) mock 對象并且你可以通過(guò)正常方式來(lái)配置它:

>>>
>>> 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 被調用時(shí)將被調用的一個(gè)函數,可調用對象或者要被引發(fā)的異常(類(lèi)或實(shí)例)。

如果你傳入一個(gè)函數則它將附帶與該 mock 相同的參數被調用并且除了該函數返回 DEFAULT 單例的情況以外對該 mock 的調用都將隨后返回該函數所返回的任何東西。 如果該函數返回 DEFAULT 則該 mock 將返回其正常值 (來(lái)自 return_value)。

如果你傳入一個(gè)可迭代對象,它會(huì )被用來(lái)獲取一個(gè)在每次調用時(shí)必須產(chǎn)生一個(gè)值的迭代器。 這個(gè)值可以是一個(gè)要被引發(fā)的異常實(shí)例,或是一個(gè)要從該調用返回給 mock 的值 (DEFAULT 處理與函數的情況一致)。

一個(gè)引發(fā)異常(來(lái)測試 API 的異常處理)的 mock 的示例:

>>>
>>> mock = Mock()
>>> mock.side_effect = Exception('Boom!')
>>> mock()
Traceback (most recent call last):
  ...
Exception: Boom!

使用 side_effect 來(lái)返回包含多個(gè)值的序列:

>>>
>>> mock = Mock()
>>> mock.side_effect = [3, 2, 1]
>>> mock(), mock(), mock()
(3, 2, 1)

使用一個(gè)可調用對象:

>>>
>>> mock = Mock(return_value=3)
>>> def side_effect(*args, **kwargs):
...     return DEFAULT
...
>>> mock.side_effect = side_effect
>>> mock()
3

side_effect 可以在構造器中設置。 下面是在 mock 被調用時(shí)增加一個(gè)該屬性值并返回它的例子:

>>>
>>> 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 沒(méi)有被調用),或是 mock 最近一次被調用時(shí)附帶的參數。 這將采用元組的形式:第一個(gè)成員也可以通過(guò) args 特征屬性來(lái)訪(fǎng)問(wèn),它是 mock 被調用時(shí)所附帶的任何位置參數 (或為空元組),而第二個(gè)成員也可以通過(guò) kwargs 特征屬性來(lái)訪(fǎng)問(wèn),它則是任何關(guān)鍵字參數 (或為空字典)。

>>>
>>> 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_callsmock_calls 的成員都是 call 對象。 這些對象屬性元組,因此它們可被解包以獲得單獨的參數并創(chuàng )建更復雜的斷言。 參見(jiàn) 作為元組的 call。

在 3.8 版更改: 增加了 argskwargs 特征屬性。properties.

call_args_list?

這是一個(gè)已排序的對 mock 對象的所有調用的列表(因此該列表的長(cháng)度就是它已被調用的次數)。 在執行任何調用之前它將是一個(gè)空列表。 call 對象可以被用來(lái)方便地構造調用列表以與 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 對象。 它們可作為元組被解包以獲得單個(gè)參數。 參見(jiàn) 作為元組的 call。

method_calls?

除了會(huì )追蹤對其自身的調用,mock 還會(huì )追蹤對方法和屬性,以及 它們的 方法和屬性的訪(fǎng)問(wèn):

>>>
>>> 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 對象。 它們可以作為元組被解包以獲得單個(gè)參數。 參見(jiàn) 作為元組的 call。

mock_calls?

mock_calls 會(huì )記錄 所有 對 mock 對象、它的方法、魔術(shù)方法的調用 以及 返回值的 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 對象。 它們可以作為元組被解包以獲得單個(gè)參數。 參見(jiàn) 作為元組的 call。

備注

mock_calls 的記錄方式意味著(zhù)在進(jìn)行嵌套調用時(shí),之前調用的形參不會(huì )被記錄因而這樣的比較將總是相等:

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

通常一個(gè)對象的 __class__ 屬性將返回其類(lèi)型。 對于具有 spec 的 mock 對象來(lái)說(shuō),__class__ 將改為返回 spec 類(lèi)。 這將允許 mock 對象為它們所替換 / 屏蔽的對象跳過(guò) isinstance() 測試:

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

__class__ 是可以被賦值的,這將允許 mock 跳過(guò) 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_valueside_effect 在不可調用的 mock 上沒(méi)有意義。

使用類(lèi)或實(shí)例作為 specspec_set 的 mock 對象能夠跳過(guò) isinstance() 測試:

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

Mock 類(lèi)具有對 mock 操作魔術(shù)方法的支持。 請參閱 魔術(shù)方法 了解完整細節。

mock 操作類(lèi)和 patch() 裝飾器都接受任意關(guān)鍵字參數用于配置。 對于 patch() 裝飾器來(lái)說(shuō)關(guān)鍵字參數會(huì )被傳給所創(chuàng )建 mock 的構造器。 這些關(guān)鍵字被用于配置 mock 的屬性:

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

子 mock 的返回值和附帶效果也可使用帶點(diǎn)號的標記通過(guò)相同的方式來(lái)設置。 由于你無(wú)法直接在調用中使用帶點(diǎn)號的名稱(chēng)因此你需要創(chuàng )建一個(gè)字典并使用 ** 來(lái)解包它:

>>>
>>> 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) 創(chuàng )建的可調用 mock 將在匹配調用與 mock 時(shí)內省規格說(shuō)明對象的簽名。 因此,它可以匹配實(shí)際調用的參數而不必關(guān)心它們是按位置還是按名稱(chēng)傳入的:

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

這適用于 assert_called_with(), assert_called_once_with(), assert_has_calls()assert_any_call()。 當執行 Autospeccing 時(shí),它還將應用于 mock 對象的方法調用。

在 3.4 版更改: 添加了在附帶規格說(shuō)明和自動(dòng)規格說(shuō)明的 mock 對象上的簽名內省

class unittest.mock.PropertyMock(*args, **kwargs)?

旨在作為類(lèi)的特征屬性或其他描述器使用的 mock。 PropertyMock 提供了 __get__()__set__() 方法以便你可以在它被提取時(shí)指定一個(gè)返回值。

當從一個(gè)對象提取 PropertyMock 實(shí)例時(shí)將不附帶任何參數地調用該 mock。 如果設置它則調用該 mock 時(shí)將附帶被設置的值。

>>>
>>> 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 屬性的存儲方式你無(wú)法直接將 PropertyMock 附加到一個(gè) mock 對象。 但是你可以將它附加到 mock 類(lèi)型對象:

>>>
>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> type(m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()
class unittest.mock.AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)?

MagicMock 的異步版本。 AsyncMock 對象的行為方式將使該對象被識別為異步函數,其調用的結果將為可等待對象。

>>>
>>> mock = AsyncMock()
>>> asyncio.iscoroutinefunction(mock)
True
>>> inspect.isawaitable(mock())  
True

調用 mock() 的結果是一個(gè)異步函數,它在被等待之后將具有 side_effectreturn_value 的結果:

  • 如果 side_effect 是一個(gè)函數,則異步函數將返回該函數的結果,

  • 如果 side_effect 是一個(gè)異常,則異步函數將引發(fā)該異常,

  • 如果 side_effect 是一個(gè)可迭代對象,則異步函數將返回該可迭代對象的下一個(gè)值,但是,如果結果序列被耗盡,則會(huì )立即引發(fā) StopAsyncIteration,

  • 如果 side_effect 未被定義,則異步函數將返回is not defined, the async function will return the value defined by return_value 所定義的值,因而,在默認情況下,異步函數會(huì )返回一個(gè)新的 AsyncMock 對象。

MockMagicMockspec 設為異步函數將導致在調用后返回一個(gè)協(xié)程對象。

>>>
>>> async def async_func(): pass
...
>>> mock = MagicMock(async_func)
>>> mock
<MagicMock spec='function' id='...'>
>>> mock()  
<coroutine object AsyncMockMixin._mock_call at ...>

Mock, MagicMockAsyncMockspec 設為帶有異步和同步函數的類(lèi)將自動(dòng)刪除其中的同步函數并將它們設為 MagicMock (如果上級 mock 是 AsyncMockMagicMock) 或者 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='...'>

3.8 新版功能.

assert_awaited()?

斷言 mock 已被等待至少一次。 請注意這是從被調用的對象中分離出來(lái)的,必須要使用 await 關(guān)鍵字:

>>>
>>> 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.method.assert_awaited_once()
Traceback (most recent call last):
...
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
assert_awaited_with(*args, **kwargs)?

斷言上一次等待傳入了指定的參數。

>>>
>>> mock = AsyncMock()
>>> async def main(*args, **kwargs):
...     await mock(*args, **kwargs)
...
>>> asyncio.run(main('foo', bar='bar'))
>>> mock.assert_awaited_with('foo', bar='bar')
>>> mock.assert_awaited_with('other')
Traceback (most recent call last):
...
AssertionError: expected call not found.
Expected: mock('other')
Actual: mock('foo', bar='bar')
assert_awaited_once_with(*args, **kwargs)?

斷言 mock 已被等待恰好一次并且附帶了指定的參數。

>>>
>>> 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 為假值則等待必須是順序進(jìn)行的。 在指定的等待之前或之后還可以有額外的調用。

如果 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 從未被等待過(guò)。

>>>
>>> mock = AsyncMock()
>>> mock.assert_not_awaited()
reset_mock(*args, **kwargs)?

參見(jiàn) Mock.reset_mock()。 還會(huì )將 await_count 設為 0,將 await_args 設為 None,并清空 await_args_list。

await_count?

一個(gè)追蹤 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 對象按順序執行的所有等待組成的列表(因此該列表的長(cháng)度即它被等待的次數)。 在有任何等待被執行之前它將為一個(gè)空列表。

>>>
>>> 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')]

調用?

Mock 對象是可調用對象。 調用將把值集合作為 return_value 屬性返回。 默認的返回值是一個(gè)新的 Mock 對象;它會(huì )在對返回值的首次訪(fǎng)問(wèn)(不論是顯式訪(fǎng)問(wèn)還是通過(guò)調用 Mock)時(shí)被創(chuàng )建 —— 但它會(huì )被保存并且每次都返回相同的對象。

對該對象的調用將被記錄在 call_argscall_args_list 等屬性中。

如果設置了 side_effect 則它將在調用被記錄之后被調用,因此如果 side_effect 引發(fā)了異常該調用仍然會(huì )被記錄。

讓一個(gè) mock 在被調用時(shí)引發(fā)異常的最簡(jiǎn)單方式是將 side_effect 設為一個(gè)異常類(lèi)或實(shí)例:

>>>
>>> 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 函數在被調用時(shí)將附帶與該 mock 相同的參數。 這允許你根據輸入動(dòng)態(tài)地改變返回值:

>>>
>>> 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 仍然返回默認的返回值(一個(gè)新的 mock對象),或是任何設定的返回值,那么有兩種方式可以做到這一點(diǎn)。 從 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

要移除一個(gè) 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 的重復調用將返回來(lái)自該可迭代對象的值(直到該可迭代對象被耗盡并導致 StopIteration 被引發(fā)):

>>>
>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
  ...
StopIteration

如果該可迭代對象有任何成員屬于異常則它們將被引發(fā)而不是被返回:

>>>
>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
 ...
ValueError
>>> m()
66

刪除屬性?

Mock 對象會(huì )根據需要創(chuàng )建屬性。 這允許它們可以假裝成任意類(lèi)型的對象。

你可能想要一個(gè) mock 對象在調用 hasattr() 時(shí)返回 False,或者在獲取某個(gè)屬性時(shí)引發(fā) AttributeError。 你可以通過(guò)提供一個(gè)對象作為 mock 的 spec 屬性來(lái)做到這點(diǎn),但這并不總是很方便。

你可以通過(guò)刪除屬性來(lái)“屏蔽”它們。 屬性一旦被刪除,訪(fǎng)問(wèn)它將引發(fā) 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 的名稱(chēng)與 name 屬性?

由于 "name" 是 Mock 構造器的參數之一,如果你想讓你的 mock 對象具有 "name" 屬性你不可以在創(chuàng )建時(shí)傳入該參數。 有兩個(gè)替代方式。 一個(gè)選項是使用 configure_mock():

>>>
>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'

一個(gè)更簡(jiǎn)單的選項是在 mock 創(chuàng )建之后簡(jiǎn)單地設置 "name" 屬性:

>>>
>>> mock = MagicMock()
>>> mock.name = "foo"

附加 Mock 作為屬性?

當你附加一個(gè) mock 作為另一個(gè) mock 的屬性(或作為返回值)時(shí)它會(huì )成為該 mock 的 "子對象"。 對子對象的調用會(huì )被記錄在父對象的 method_callsmock_calls 屬性中。 這適用于配置子 mock 然后將它們附加到父對象,或是將 mock 附加到將記錄所有對子對象的調用的父對象上并允許你創(chuàng )建有關(guān) 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)]

這里有一個(gè)例外情況是如果 mock 設置了名稱(chēng)。 這允許你在出于某些理由不希望其發(fā)生時(shí)避免 "父對象" 的影響。

>>>
>>> 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
[]

通過(guò) patch() 創(chuàng )建的 mock 會(huì )被自動(dòng)賦予名稱(chēng)。 要將具有名稱(chēng)的 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

僅有的例外是魔術(shù)方法和屬性(其名稱(chēng)前后都帶有雙下劃線(xiàn))。 Mock 不會(huì )創(chuàng )建它們而是將引發(fā) AttributeError。 這是因為解釋器將會(huì )經(jīng)常隱式地請求這些方法,并且在它準備接受一個(gè)魔術(shù)方法卻得到一個(gè)新的 Mock 對象時(shí)會(huì ) 相當 困惑。 如果你需要魔術(shù)方法支持請參閱 魔術(shù)方法。

patch 裝飾器?

patch 裝飾器僅被用于在它們所裝飾的函數作用域內部為對象添加補丁。 它們會(huì )自動(dòng)為你執行去除補丁的處理,即使是在引發(fā)了異常的情況下。 所有這些函數都還可在 with 語(yǔ)句中使用或是作為類(lèi)裝飾器。

patch?

備注

問(wèn)題的關(guān)鍵是要在正確的命名空間中打補丁。 參見(jiàn) where to patch 一節。

unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)?

patch() 可以作為函數裝飾器、類(lèi)裝飾器或上下文管理器。 在函數或 with 語(yǔ)句的內部,target 會(huì )打上一個(gè) new 對象補丁。 當函數/with 語(yǔ)句退出時(shí)補丁將被撤銷(xiāo)。

如果 new 被省略,那么如果被打補丁的對象是一個(gè)異步函數則 target 將被替換為 AsyncMock 否則替換為 MagicMock。 如果 patch() 被用作裝飾器并且 new 被省略,那么已創(chuàng )建的 mock 將作為一個(gè)附加參數傳入被裝飾的函數。 如果 patch() 被用作上下文管理器那么已創(chuàng )建的 mock 將被該上下文管理器所返回。is returned by the context manager.

target 應當為 'package.module.ClassName' 形式的字符串。 target 將被導入并且該指定對象會(huì )被替換為 new 對象,因此 target 必須是可以從你調用 patch() 的環(huán)境中導入的。 target 會(huì )在被裝飾的函數被執行的時(shí)候被導入,而非在裝飾的時(shí)候。

specspec_set 關(guān)鍵字參數會(huì )被傳遞給 MagicMock,如果 patch 為你創(chuàng )建了此對象的話(huà)。

此外你還可以傳入 spec=Truespec_set=True,這將使 patch 將被模擬的對象作為 spec/spec_set 對象傳入。

new_callable 允許你指定一個(gè)不同的類(lèi),或者可調用對象,它將被調用以創(chuàng )建 新的 對象。 在默認情況下將指定 AsyncMock 用于異步函數,MagicMock 用于其他函數。

另一種更強形式的 specautospec。 如果你設置了 autospec=True 則將以來(lái)自被替換對象的 spec 來(lái)創(chuàng )建 mock。 mock 的所有屬性也將具有被替換對象相應屬性的 spec。 被模擬的方法和函數將檢查它們的參數并且如果使用了錯誤的簽名調用它們則將引發(fā) TypeError。 對于替換了一個(gè)類(lèi)的 mock,它們的返回值(即‘實(shí)例’)將具有與該類(lèi)相同的 spec。 請參閱 create_autospec() 函數以及 Autospeccing。

除了 autospec=True 你還可以傳入 autospec=some_object 以使用任意對象而不是被替換的對象作為 spec。

在默認情況下 patch() 將無(wú)法替換不存在的屬性。 如果你傳入 create=True,且該屬性并不存在,則 patch 將在調用被打補丁的函數時(shí)為你創(chuàng )建該屬性,并在退出被打補丁的函數時(shí)再次刪除它。 這適用于編寫(xiě)針對生產(chǎn)代碼在運行時(shí)創(chuàng )建的屬性的測試。 它默認是被關(guān)閉的因為這具有危險性。 當它被開(kāi)啟時(shí)你將能夠針對實(shí)際上并不存在的 API 編寫(xiě)通過(guò)測試!

備注

在 3.5 版更改: 如果你要給某個(gè)模塊的內置函數打補丁則不必傳入 create=True,它默認就會(huì )被添加。

Patch 可以被用作 TestCase 類(lèi)裝飾器。 其作用是裝飾類(lèi)中的每個(gè)測試方法。 當你的測試方法共享同一個(gè)補丁集時(shí)這將減少模板代碼。 patch() 會(huì )通過(guò)查找以 patch.TEST_PREFIX 打頭的名稱(chēng)來(lái)找到測試。 其默認值為 'test',這與 unittest 打到測試的方式一致。 你可以通過(guò)設置 patch.TEST_PREFIX 來(lái)指定其他的前綴。

Patch 可以通過(guò) with 語(yǔ)句作為上下文管理器使用。 這時(shí)補丁將應用于 with 語(yǔ)句的縮進(jìn)代碼塊。 如果你使用了 "as" 則打補丁的對象將被綁定到 "as" 之后的名稱(chēng);這非常適用于當 patch() 為你創(chuàng )建 mock 對象的情況。

patch() 可接受任意關(guān)鍵字參數。 如果打補丁的對象是異步的則這些參數將被傳給 AsyncMock,否則傳給 MagicMock,或者是指定的 new_callable。

patch.dict(...), patch.multiple(...)patch.object(...) 可用于其他使用場(chǎng)景。

patch() 作為函數裝飾器,為你創(chuàng )建 mock 并將其傳入被裝飾的函數:

>>>
>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
...     print(mock_class is SomeClass)
...
>>> function(None)
True

為類(lèi)打補丁將把該類(lèi)替換為 MagicMock實(shí)例。 如果該類(lèi)是在受測試的代碼中被實(shí)例化的則它將為所要使用的 mock 的 return_value。

如果該類(lèi)被多次實(shí)例化則你可以使用 side_effect 來(lái)每次返回一個(gè)新 mock。 或者你也可以將 return_value 設為你希望的任何對象。

要在被打補丁的類(lèi)的 實(shí)例 的方法上配置返回值你必須在 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,那么所創(chuàng )建的 mock 的返回值將具有同樣的 spec。

>>>
>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()

new_callable 參數適用于當你想要使用其他類(lèi)來(lái)替代所創(chuàng )建的 mock 默認的 MagicMock 的場(chǎng)合。 例如,如果你想要使用 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

另一個(gè)使用場(chǎng)景是用 io.StringIO 實(shí)例來(lái)替換某個(gè)對象:

>>>
>>> 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() 為你創(chuàng )建 mock 時(shí),通常你需要做的第一件事就是配置該 mock。 某些配置可以在對 patch 的調用中完成。 你在調用時(shí)傳入的任何關(guān)鍵字參數都將被用來(lái)在所創(chuàng )建的 mock 上設置屬性:

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

除了所創(chuàng )建的 mock 的屬性上的屬性,例如 return_valueside_effect,還可以配置子 mock 的屬性。 將這些屬性直接作為關(guān)鍵字參數傳入在語(yǔ)義上是無(wú)效的,但是仍然能夠使用 ** 將以這些屬性為鍵的字典擴展至一個(gè) 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

在默認情況下,嘗試給某個(gè)模塊中并不存在的函數(或者某個(gè)類(lèi)中的方法或屬性)打補丁將會(huì )失敗并引發(fā) 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_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() 現在將返回一個(gè) AsyncMock。

patch.object?

patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)?

用一個(gè) mock 對象為對象 (target) 中指定名稱(chēng)的成員 (attribute) 打補丁。

patch.object() 可以被用作裝飾器、類(lèi)裝飾器或上下文管理器。 new, spec, create, spec_set, autospecnew_callable 等參數的含義與 patch() 的相同。 與 patch() 類(lèi)似,patch.object() 接受任意關(guān)鍵字參數用于配置它所創(chuàng )建的 mock 對象。

當用作類(lèi)裝飾器時(shí) patch.object() 將認可 patch.TEST_PREFIX 作為選擇所要包裝方法的標準。

你可以附帶三個(gè)參數或兩個(gè)參數來(lái)調用 patch.object()。 三個(gè)參數的形式將接受要打補丁的對象、屬性的名稱(chēng)以及將要替換該屬性的對象。

將附帶兩個(gè)參數的形式調用時(shí)你將省略替換對象,還會(huì )為你創(chuàng )建一個(gè) 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)?

為一個(gè)字典或字典類(lèi)對象打補丁,并在測試之后將該目錄恢復到其初始狀態(tài)。

in_dict 可以是一個(gè)字典或映射類(lèi)容器。 如果它是一個(gè)映射則它必須至少支持獲取、設置和刪除條目以及對鍵執行迭代。

in_dict 也可以是一個(gè)指定字典名稱(chēng)的字符串,然后將通過(guò)導入操作來(lái)獲取該字典。

values 可以是一個(gè)要在字典中設置的值的字典。 values 也可以是一個(gè)包含 (key, value) 對的可迭代對象。

如果 clear 為真值則該字典將在設置新值之前先被清空。

patch.dict() 也可以附帶任意關(guān)鍵字參數調用以設置字典中的值。

在 3.8 版更改: 現在當 patch.dict() 被用作上下文管理器時(shí)將返回被打補丁的字典。now returns the patched dictionary when used as a context manager.

patch.dict() 可被用作上下文管理器、裝飾器或類(lèi)裝飾器:

>>>
>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
...     assert foo == {'newkey': 'newvalue'}
>>> test()
>>> assert foo == {}

當被用作類(lèi)裝飾器時(shí) 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')

如果你在為你的測試使用不同的前綴,你可以通過(guò)設置 patch.TEST_PREFIX 來(lái)將不同的前綴告知打補丁方。 有關(guān)如何修改該值的詳情請參閱 TEST_PREFIX。

patch.dict() 可被用來(lái)向一個(gè)字典添加成員,或者簡(jiǎn)單地讓測試修改一個(gè)字典,并確保當測試結束時(shí)恢復該字典。

>>>
>>> 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() 調用中使用關(guān)鍵字來(lái)設置字典的值:

>>>
>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
...     import mymodule
...     mymodule.function('some', 'args')
...
'fish'

patch.dict() 可以用于實(shí)際上不是字典的字典類(lèi)對象。 它們最少必須支持條目獲取、設置、刪除以及迭代或成員檢測兩者中的一個(gè)。 這對應于魔術(shù)方法 __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)?

在單個(gè)調用中執行多重補丁。 它接受要打補丁的對象(一個(gè)對象或一個(gè)通過(guò)導入來(lái)獲取對象的字符串)以及用于補丁的關(guān)鍵字參數:

with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'):
    ...

如果你希望 patch.multiple() 為你創(chuàng )建 mock 則要使用 DEFAULT 作為值。 在此情況下所創(chuàng )建的 mock 會(huì )通過(guò)關(guān)鍵字參數傳入被裝飾的函數,而當 patch.multiple() 被用作上下文管理器時(shí)則將返回一個(gè)字典。

patch.multiple() 可以被用作裝飾器、類(lèi)裝飾器或上下文管理器。 spec, spec_set, create, autospecnew_callable 等參數的含義與 patch() 的相同。 這些參數將被應用到 patch.multiple() 所打的 所有 補丁。

當被用作類(lèi)裝飾器時(shí) patch.multiple() 將認可 patch.TEST_PREFIX 作為選擇所要包裝方法的標準。

如果你希望 patch.multiple() 為你創(chuàng )建 mock,那么你可以使用 DEFAULT 作為值。 如果你使用 patch.multiple() 作為裝飾器則所創(chuàng )建的 mock 會(huì )作為關(guān)鍵字參數傳入被裝飾的函數。

>>>
>>> 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 裝飾器嵌套使用,但要將作為關(guān)鍵字傳入的參數要放在 patch() 所創(chuàng )建的標準參數 之后:

>>>
>>> @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() 被用作上下文管理器,則上下文管理器的返回值將是一個(gè)以所創(chuàng )建的 mock 的名稱(chēng)為鍵的字典:

>>>
>>> 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?

所有補丁對象都具有 start()stop() 方法。 使用這些方法可以更簡(jiǎn)單地在 setUp 方法上打補丁,還可以讓你不必嵌套使用裝飾器或 with 語(yǔ)句就能打多重補丁。

要使用這些方法請按正常方式調用 patch(), patch.object()patch.dict() 并保留一個(gè)指向所返回 patcher 對象的引用。 然后你可以調用 start() 來(lái)原地打補丁并調用 stop() 來(lái)恢復它。

如果你使用 patch() 來(lái)創(chuàng )建自己的 mock 那么將可通過(guò)調用 patcher.start 來(lái)返回它。

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

此操作的一個(gè)典型應用場(chǎng)景是在一個(gè) TestCasesetUp 方法中執行多重補丁:

>>>
>>> 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()

小心

如果你要使用這個(gè)技巧則你必須通過(guò)調用 stop 來(lái)確保補丁被“恢復”。 這可能要比你想像的更麻煩,因為如果在 setUp 中引發(fā)了異常那么 tearDown 將不會(huì )被調用。 unittest.TestCase.addCleanup() 可以簡(jiǎn)化此操作:

>>>
>>> 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 對象的引用。

還可以通過(guò)使用 patch.stopall() 來(lái)停止已啟動(dòng)的所有補丁。

patch.stopall()?

停止所有激活的補丁。 僅會(huì )停止通過(guò) start 啟動(dòng)的補丁。

為內置函數打補丁?

你可以為一個(gè)模塊中的任何內置函數打補丁。 以以示例是為內置函數 ord() 打補丁:

>>>
>>> @patch('__main__.ord')
... def test(mock_ord):
...     mock_ord.return_value = 101
...     print(ord('c'))
...
>>> test()
101

TEST_PREFIX?

所有補丁都可被用作類(lèi)裝飾器。 當以這種方式使用時(shí)它們將會(huì )包裝類(lèi)中的每個(gè)測試方法。 補丁會(huì )將以名字以 'test' 開(kāi)頭的方法識別為測試方法。 這與 unittest.TestLoader 查找測試方法的默認方式相同。

你可能會(huì )想要為你的測試使用不同的前綴。 你可以通過(guò)設置 patch.TEST_PREFIX 來(lái)告知打補丁方不同的前綴:

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

嵌套補丁裝飾器?

如果你想要應用多重補丁那么你可以簡(jiǎn)單地堆疊多個(gè)裝飾器。

你可以使用以下模式來(lái)堆疊多個(gè)補丁裝飾器:

>>>
>>> @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 應用裝飾器的標準方式。 被創(chuàng )建并傳入你的測試函數的 mock 的順序也將匹配這個(gè)順序。

補丁的目標?

patch() 通過(guò)(臨時(shí)性地)修改某一個(gè)對象的 名稱(chēng) 指向另一個(gè)對象來(lái)發(fā)揮作用。 可以有多個(gè)名稱(chēng)指向任意單獨對象,因此要讓補丁起作用你必須確保已為被測試的系統所使用的名稱(chēng)打上補丁。

基本原則是你要在對象 被查找 的地方打補丁,這不一定就是它被定義的地方。 一組示例將有助于厘清這一點(diǎn)。

想像我們有一個(gè)想要測試的具有如下結構的項目:

a.py
    -> Defines SomeClass

b.py
    -> from a import SomeClass
    -> some_function instantiates SomeClass

現在我們要測試 some_function 但我們想使用 patch() 來(lái)模擬 SomeClass。 問(wèn)題在于當我們導入模塊 b 時(shí),我們將必須讓它從模塊 a 導入 SomeClass。 如果我們使用 patch() 來(lái)模擬 a.SomeClass 那么它將不會(huì )對我們的測試造成影響;模塊 b 已經(jīng)擁有對 真正的 SomeClass 的引用因此看上去我們的補丁不會(huì )有任何影響。

關(guān)鍵在于對 SomeClass 打補丁操作是在它被使用(或它被查找)的地方。 在此情況下實(shí)際上 some_function 將在模塊 b 中查找 SomeClass,而我們已經(jīng)在那里導入了它。 補丁看上去應該是這樣:

@patch('b.SomeClass')

但是,再考慮另一個(gè)場(chǎng)景,其中不是 from a import SomeClass 而是模塊 b 執行了 import a 并且 some_function 使用了 a.SomeClass。 這兩個(gè)導入形式都很常見(jiàn)。 在這種情況下我們要打補丁的類(lèi)將在該模塊中被查找因而我們必須改為對 a.SomeClass 打補丁:

@patch('a.SomeClass')

對描述器和代理對象打補丁?

patchpatch.object 都能正確地對描述器打補丁并恢復:包括類(lèi)方法、靜態(tài)方法和特征屬性。 你應當在 類(lèi) 而不是在實(shí)例上為它們打補丁。 它們還適用于代理屬性訪(fǎng)問(wèn)的 某些 對象,例如 django 設置對象。

MagicMock 與魔術(shù)方法支持?

模擬魔術(shù)方法?

Mock 支持模擬 Python 協(xié)議方法,或稱(chēng)“魔術(shù)方法”。 這允許 mock 對象替代容器或其他實(shí)現了 Python 協(xié)議的對象。

因為查找魔術(shù)方法的方式不同于普通方法 2,這種支持采用了特別的實(shí)現。 這意味著(zhù)只有特定的魔術(shù)方法受到支持。 受支持的是 幾乎 所有魔術(shù)方法。 如果有你需要但被遺漏的請告知我們。

你可以通過(guò)在某個(gè)函數或 mock 實(shí)例中設置魔術(shù)方法來(lái)模擬它們。 如果你是使用函數則它 必須 接受 self 作為第一個(gè)參數 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)
[]

一個(gè)這樣的應用場(chǎng)景是在 with 語(yǔ)句中模擬作為上下文管理器的對象:

>>>
>>> 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)

對魔術(shù)方法的調用不會(huì )在 method_calls 中出現,但它們會(huì )被記錄在 mock_calls 中。

備注

如果你使用 spec 關(guān)鍵字參數來(lái)創(chuàng )建 mock 那么嘗試設置不包含在 spec 中的魔術(shù)方法將引發(fā) AttributeError。

受支持魔術(shù)方法的完整列表如下:

  • __hash__, __sizeof__, __repr____str__

  • __dir__, __format____subclasses__

  • __round__, __floor__, __trunc____ceil__

  • 比較運算: __lt__, __gt__, __le__, __ge__, __eq____ne__

  • 容器方法: __getitem__, __setitem__, __delitem__, __contains__, __len__, __iter__, __reversed____missing__

  • 上下文管理器: __enter__, __exit__, __aenter____aexit__

  • 單目數值運算方法: __neg__, __pos____invert__

  • The numeric methods (including right hand and in-place variants): __add__, __sub__, __mul__, __matmul__, __truediv__, __floordiv__, __mod__, __divmod__, __lshift__, __rshift__, __and__, __xor__, __or__, and __pow__

  • 數值轉換方法: __complex__, __int__, __float____index__

  • 描述器方法: __get__, __set__ and __delete__

  • 封存方法: __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate____setstate__

  • 文件系統路徑表示: __fspath__

  • 異步迭代方法: __aiter__ and __anext__

在 3.8 版更改: 增加了對 os.PathLike.__fspath__() 的支持。

在 3.8 版更改: 增加了對 __aenter__, __aexit__, __aiter____anext__ 的支持。

下列方法均存在但是 不受 支持,因為它們或者被 mock 所使用,或者無(wú)法動(dòng)態(tài)設置,或者可能導致問(wèn)題:

  • __getattr__, __setattr__, __init____new__

  • __prepare__, __instancecheck__, __subclasscheck__, __del__

MagicMock?

存在兩個(gè)版本的 MagicMock: MagicMockNonCallableMagicMock.

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

MagicMock 是包含了大部分魔術(shù)方法的默認實(shí)現的 Mock 的子類(lèi)。 你可以使用 MagicMock 而無(wú)須自行配置魔術(shù)方法。without having to configure the magic methods yourself.

構造器形參的含義與 Mock 的相同。

如果你使用了 specspec_set 參數則將 只有 存在于 spec 中的魔術(shù)方法會(huì )被創(chuàng )建。

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

MagicMock 的不可調用版本。

其構造器的形參具有與 MagicMock 相同的含義,區別在于 return_valueside_effect 在不可調用的 mock 上沒(méi)有意義。

魔術(shù)方法是通過(guò) MagicMock 對象來(lái)設置的,因此你可以用通常的方式來(lái)配置它們并使用它們:

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

在默認情況下許多協(xié)議方法都需要返回特定類(lèi)型的對象。 這些方法都預先配置了默認的返回值,以便它們在你對返回值不感興趣時(shí)可以不做任何事就能被使用。 如果你想要修改默認值則你仍然可以手動(dòng) 設置 返回值。

方法及其默認返回值:

  • __lt__: NotImplemented

  • __gt__: NotImplemented

  • __le__: NotImplemented

  • __ge__: NotImplemented

  • __int__: 1

  • __contains__: False

  • __len__: 0

  • __iter__: iter([])

  • __exit__: False

  • __aexit__: False

  • __complex__: 1j

  • __float__: 1.0

  • __bool__: True

  • __index__: 1

  • __hash__: mock 的默認 hash

  • __str__: mock 的默認 str

  • __sizeof__: mock 的默認 sizeof

例如:

>>>
>>> mock = MagicMock()
>>> int(mock)
1
>>> len(mock)
0
>>> list(mock)
[]
>>> object() in mock
False

兩個(gè)相等性方法 __eq__()__ne__() 是特殊的。 它們基于標識號進(jìn)行默認的相等性比較,使用 side_effect 屬性,除非你修改它們的返回值以返回其他內容:

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

如果返回值 迭代器,則對其執行一次迭代就會(huì )將它耗盡因而后續執行的迭代將會(huì )輸出空列表:

>>>
>>> mock.__iter__.return_value = iter(['a', 'b', 'c'])
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
[]

MagicMock 已配置了所有受支持的魔術(shù)方法,只有某些晦澀和過(guò)時(shí)的魔術(shù)方法是例外。 如果你需要仍然可以設置它們。

MagicMock 中受到支持但默認未被設置的魔術(shù)方法有:

  • __subclasses__

  • __dir__

  • __format__

  • __get__, __set____delete__

  • __reversed____missing__

  • __reduce__, __reduce_ex__, __getinitargs__, __getnewargs__, __getstate____setstate__

  • __getformat__

2

魔術(shù)方法 應當 是在類(lèi)中而不是在實(shí)例中查找。 不同的 Python 版本對這個(gè)規則的應用并不一致。 受支持的協(xié)議方法應當適用于所有受支持的 Python 版本。

3

該函數基本上是與類(lèi)掛鉤的,但每個(gè) Mock 實(shí)例都會(huì )與其他實(shí)例保持隔離。

輔助對象?

sentinel?

unittest.mock.sentinel?

sentinel 對象提供了一種為你的測試提供獨特對象的便捷方式。

屬性是在你通過(guò)名稱(chēng)訪(fǎng)問(wèn)它們時(shí)按需創(chuàng )建的。 訪(fǎng)問(wèn)相同的屬性將始終返回相同的對象。 返回的對象會(huì )有一個(gè)合理的 repr 以使測試失敗消息易于理解。

在 3.7 版更改: 現在 sentinel 屬性會(huì )在它們被 copypickle 時(shí)保存其標識。

在測試時(shí)你可能需要測試是否有一個(gè)特定的對象作為參數被傳給了另一個(gè)方法,或是被其返回。 通常的做法是創(chuàng )建一個(gè)指定名稱(chēng)的 sentinel 對象來(lái)執行這種測試。 sentinel 提供了一種創(chuàng )建和測試此類(lèi)對象的標識的便捷方式。

在這個(gè)示例中我們?yōu)?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
>>> result
sentinel.some_object

DEFAULT?

unittest.mock.DEFAULT?

DEFAULT 對象是一個(gè)預先創(chuàng )建的 sentinel (實(shí)際為 sentinel.DEFAULT)。 它可被 side_effect 函數用來(lái)指明其應當使用正常的返回值。

call?

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

call() 是一個(gè)可創(chuàng )建更簡(jiǎn)單斷言的輔助對象,用于同 call_args, call_args_list, mock_callsmethod_calls 進(jìn)行比較。 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()?

對于代表多個(gè)調用的 call 對象,call_list() 將返回一個(gè)包含所有中間調用以及最終調用的列表。

call_list 特別適用于創(chuàng )建針對“鏈式調用”的斷言。 鏈式調用是指在一行代碼中執行的多個(gè)調用。 這將使得一個(gè) mock 的中存在多個(gè)條目 mock_calls。 手動(dòng)構造調用的序列將會(huì )很煩瑣。

call_list() 可以根據同一個(gè)鏈式調用構造包含多個(gè)調用的序列:

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

A call object is either a tuple of (positional args, keyword args) or (name, positional args, keyword args) depending on how it was constructed. When you construct them yourself this isn't particularly interesting, but the call objects that are in the Mock.call_args, Mock.call_args_list and Mock.mock_calls attributes can be introspected to get at the individual arguments they contain.

The call objects in Mock.call_args and Mock.call_args_list are two-tuples of (positional args, keyword args) whereas the call objects in Mock.mock_calls, along with ones you construct yourself, are three-tuples of (name, positional args, keyword args).

You can use their "tupleness" to pull out the individual arguments for more complex introspection and assertions. The positional arguments are a tuple (an empty tuple if there are no positional arguments) and the keyword arguments are a dictionary:

>>>
>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> kall.args
(1, 2, 3)
>>> kall.kwargs
{'arg': 'one', 'arg2': 'two'}
>>> kall.args is kall[0]
True
>>> kall.kwargs is kall[1]
True
>>>
>>> m = MagicMock()
>>> m.foo(4, 5, 6, arg='two', arg2='three')
<MagicMock name='mock.foo()' id='...'>
>>> kall = m.mock_calls[0]
>>> name, args, kwargs = kall
>>> name
'foo'
>>> args
(4, 5, 6)
>>> kwargs
{'arg': 'two', 'arg2': 'three'}
>>> name is m.mock_calls[0][0]
True

create_autospec?

unittest.mock.create_autospec(spec, spec_set=False, instance=False, **kwargs)?

Create a mock object using another object as a spec. Attributes on the mock will use the corresponding attribute on the spec object as their spec.

Functions or methods being mocked will have their arguments checked to ensure that they are called with the correct signature.

If spec_set is True then attempting to set attributes that don't exist on the spec object will raise an AttributeError.

If a class is used as a spec then the return value of the mock (the instance of the class) will have the same spec. You can use a class as the spec for an instance object by passing instance=True. The returned mock will only be callable if instances of the mock are callable.

create_autospec() also takes arbitrary keyword arguments that are passed to the constructor of the created mock.

See Autospeccing for examples of how to use auto-speccing with create_autospec() and the autospec argument to patch().

在 3.8 版更改: create_autospec() now returns an AsyncMock if the target is an async function.

ANY?

unittest.mock.ANY?

Sometimes you may need to make assertions about some of the arguments in a call to mock, but either not care about some of the arguments or want to pull them individually out of call_args and make more complex assertions on them.

To ignore certain arguments you can pass in objects that compare equal to everything. Calls to assert_called_with() and assert_called_once_with() will then succeed no matter what was passed in.

>>>
>>> mock = Mock(return_value=None)
>>> mock('foo', bar=object())
>>> mock.assert_called_once_with('foo', bar=ANY)

ANY can also be used in comparisons with call lists like 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 is a module level variable that controls the way mock objects respond to dir(). The default is True, which uses the filtering described below, to only show useful members. If you dislike this filtering, or need to switch it off for diagnostic purposes, then set mock.FILTER_DIR = False.

With filtering on, dir(some_mock) shows only useful attributes and will include any dynamically created attributes that wouldn't normally be shown. If the mock was created with a spec (or autospec of course) then all the attributes from the original are shown, even if they haven't been accessed yet:

>>>
>>> dir(Mock())
['assert_any_call',
 'assert_called',
 'assert_called_once',
 'assert_called_once_with',
 'assert_called_with',
 'assert_has_calls',
 'assert_not_called',
 'attach_mock',
 ...
>>> from urllib import request
>>> dir(Mock(spec=request))
['AbstractBasicAuthHandler',
 'AbstractDigestAuthHandler',
 'AbstractHTTPHandler',
 'BaseHandler',
 ...

Many of the not-very-useful (private to Mock rather than the thing being mocked) underscore and double underscore prefixed attributes have been filtered from the result of calling dir() on a Mock. If you dislike this behaviour you can switch it off by setting the module level switch 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__',
 ...

Alternatively you can just use vars(my_mock) (instance members) and dir(type(my_mock)) (type members) to bypass the filtering irrespective of mock.FILTER_DIR.

mock_open?

unittest.mock.mock_open(mock=None, read_data=None)?

A helper function to create a mock to replace the use of open(). It works for open() called directly or used as a context manager.

The mock argument is the mock object to configure. If None (the default) then a MagicMock will be created for you, with the API limited to methods or attributes available on standard file handles.

read_data is a string for the read(), readline(), and readlines() methods of the file handle to return. Calls to those methods will take data from read_data until it is depleted. The mock of these methods is pretty simplistic: every time the mock is called, the read_data is rewound to the start. If you need more control over the data that you are feeding to the tested code you will need to customize this mock for yourself. When that is insufficient, one of the in-memory filesystem packages on PyPI can offer a realistic filesystem for testing.

在 3.4 版更改: Added readline() and readlines() support. The mock of read() changed to consume read_data rather than returning it on each call.

在 3.5 版更改: read_data is now reset on each call to the mock.

在 3.8 版更改: Added __iter__() to implementation so that iteration (such as in for loops) correctly consumes read_data.

Using open() as a context manager is a great way to ensure your file handles are closed properly and is becoming common:

with open('/some/path', 'w') as f:
    f.write('something')

The issue is that even if you mock out the call to open() it is the returned object that is used as a context manager (and has __enter__() and __exit__() called).

Mocking context managers with a MagicMock is common enough and fiddly enough that a helper function is useful.

>>>
>>> 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')

And for reading files:

>>>
>>> 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'

Autospeccing?

Autospeccing is based on the existing spec feature of mock. It limits the api of mocks to the api of an original object (the spec), but it is recursive (implemented lazily) so that attributes of mocks only have the same api as the attributes of the spec. In addition mocked functions / methods have the same call signature as the original so they raise a TypeError if they are called incorrectly.

Before I explain how auto-speccing works, here's why it is needed.

Mock is a very powerful and flexible object, but it suffers from two flaws when used to mock out objects from a system under test. One of these flaws is specific to the Mock api and the other is a more general problem with using mock objects.

First the problem specific to Mock. Mock has two assert methods that are extremely handy: assert_called_with() and 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.

Because mocks auto-create attributes on demand, and allow you to call them with arbitrary arguments, if you misspell one of these assert methods then your assertion is gone:

>>>
>>> mock = Mock(name='Thing', return_value=None)
>>> mock(1, 2, 3)
>>> mock.assret_called_once_with(4, 5, 6)  # Intentional typo!

Your tests can pass silently and incorrectly because of the typo.

The second issue is more general to mocking. If you refactor some of your code, rename members and so on, any tests for code that is still using the old api but uses mocks instead of the real objects will still pass. This means your tests can all pass even though your code is broken.

Note that this is another reason why you need integration tests as well as unit tests. Testing everything in isolation is all fine and dandy, but if you don't test how your units are "wired together" there is still lots of room for bugs that tests might have caught.

mock already provides a feature to help with this, called speccing. If you use a class or instance as the spec for a mock then you can only access attributes on the mock that exist on the real class:

>>>
>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with  # Intentional typo!
Traceback (most recent call last):
 ...
AttributeError: Mock object has no attribute 'assret_called_with'

The spec only applies to the mock itself, so we still have the same issue with any methods on the mock:

>>>
>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with()  # Intentional typo!

Auto-speccing solves this problem. You can either pass autospec=True to patch() / patch.object() or use the create_autospec() function to create a mock with a spec. If you use the autospec=True argument to patch() then the object that is being replaced will be used as the spec object. Because the speccing is done "lazily" (the spec is created as attributes on the mock are accessed) you can use it with very complex or deeply nested objects (like modules that import modules that import modules) without a big performance hit.

Here's an example of it in use:

>>>
>>> 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='...'>

You can see that request.Request has a spec. request.Request takes two arguments in the constructor (one of which is self). Here's what happens if we try to call it incorrectly:

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

The spec also applies to instantiated classes (i.e. the return value of specced mocks):

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

Request objects are not callable, so the return value of instantiating our mocked out request.Request is a non-callable mock. With the spec in place any typos in our asserts will raise the correct error:

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

In many cases you will just be able to add autospec=True to your existing patch() calls and then be protected against bugs due to typos and api changes.

As well as using autospec through patch() there is a create_autospec() for creating autospecced mocks directly:

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

This isn't without caveats and limitations however, which is why it is not the default behaviour. In order to know what attributes are available on the spec object, autospec has to introspect (access attributes) the spec. As you traverse attributes on the mock a corresponding traversal of the original object is happening under the hood. If any of your specced objects have properties or descriptors that can trigger code execution then you may not be able to use autospec. On the other hand it is much better to design your objects so that introspection is safe 4.

A more serious problem is that it is common for instance attributes to be created in the __init__() method and not to exist on the class at all. autospec can't know about any dynamically created attributes and restricts the api to visible attributes.

>>>
>>> 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'

There are a few different ways of resolving this problem. The easiest, but not necessarily the least annoying, way is to simply set the required attributes on the mock after creation. Just because autospec doesn't allow you to fetch attributes that don't exist on the spec it doesn't prevent you setting them:

>>>
>>> with patch('__main__.Something', autospec=True):
...   thing = Something()
...   thing.a = 33
...

There is a more aggressive version of both spec and autospec that does prevent you setting non-existent attributes. This is useful if you want to ensure your code only sets valid attributes too, but obviously it prevents this particular scenario:

>>>
>>> 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'

Probably the best way of solving the problem is to add class attributes as default values for instance members initialised in __init__(). Note that if you are only setting default attributes in __init__() then providing them via class attributes (shared between instances of course) is faster too. e.g.

class Something:
    a = 33

This brings up another issue. It is relatively common to provide a default value of None for members that will later be an object of a different type. None would be useless as a spec because it wouldn't let you access any attributes or methods on it. As None is never going to be useful as a spec, and probably indicates a member that will normally of some other type, autospec doesn't use a spec for members that are set to None. These will just be ordinary mocks (well - MagicMocks):

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

If modifying your production classes to add defaults isn't to your liking then there are more options. One of these is simply to use an instance as the spec rather than the class. The other is to create a subclass of the production class and add the defaults to the subclass without affecting the production class. Both of these require you to use an alternative object as the spec. Thankfully patch() supports this - you can simply pass the alternative object as the autospec argument:

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

This only applies to classes or already instantiated objects. Calling a mocked class to create a mock instance does not create a real instance. It is only attribute lookups - along with calls to dir() - that are done.

Sealing mocks?

unittest.mock.seal(mock)?

Seal will disable the automatic creation of mocks when accessing an attribute of the mock being sealed or any of its attributes that are already mocks recursively.

If a mock instance with a name or a spec is assigned to an attribute it won't be considered in the sealing chain. This allows one to prevent seal from fixing part of the mock object.

>>>
>>> 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 新版功能.