unittest.mock — mock 物件函式庫¶
在 3.3 版被加入.
unittest.mock 在 Python 中是一個用於進行測試的函式庫。 它允許你用 mock 物件在測試中替換部分系統,並判定它們是如何被使用的。
unittest.mock 提供了一個以 Mock 為核心的類別,去除在測試中建立大量 stubs 的需求。 在執行動作之後,你可以判定哪些 method (方法)/屬性被使用,以及有哪些引數被呼叫。 你還可以用常規的方式指定回傳值與設定所需的屬性。
此外,mock 還提供了一個 patch() 裝飾器,用於 patching 測試範圍內對 module(模組)以及 class(類別)級別的屬性,以及用於建立唯一物件的 sentinel。有關如何使用 Mock、MagicMock 和 patch() 的一些範例,請參閱快速導引。
Mock 被設計用於與 unittest 一起使用,並且基於 「action(操作) -> assertion(判定)」 模式,而不是許多 mocking 框架使用的 「record(記錄) -> replay(重播)」 模式。
對於早期版本的 Python,有一個 backport(向後移植的)unittest.mock 可以使用,可從 PyPI 下載 mock。
快速導引¶
Mock 和 MagicMock 物件在你存取它們時建立所有屬性和 method(方法),並儲存它們如何被使用的詳細訊息。你可以配置它們,以指定回傳值或限制可用的屬性,然後對它們的使用方式做出判定:
>>> 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 允許你執行 side effects,包含在 mock 被呼叫時引發例外:
>>> from unittest.mock import Mock
>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
... return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)
Mock 有許多其他方法可以讓你配置與控制它的行為。例如,spec 引數可以配置 mock ,讓其從另一個物件取得規格。嘗試讀取 mock 中不存在於規格中的屬性或方法將會失敗,並出現 AttributeError。
patch() 裝飾器/情境管理器可以在測試中簡單的 mock 模組中的類別或物件。被指定的物件在測試期間會被替換為 mock(或其他物件),並在測試結束時恢復:
>>> from unittest.mock import patch
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... def test(MockClass1, MockClass2):
... module.ClassName1()
... module.ClassName2()
... assert MockClass1 is module.ClassName1
... assert MockClass2 is module.ClassName2
... assert MockClass1.called
... assert MockClass2.called
...
>>> test()
備註
當你巢狀使用 patch 裝飾器時,mock 傳遞到被裝飾函式的順序會跟其被應用的順序相同(一般 Python 應用裝飾器的順序)。這意味著由下而上,因此在上面的範例中,module.ClassName1 的 mock 會先被傳入。
使用 patch() 時,需注意的是你得在被查找物件的命名空間中(in the namespace where they are looked up)patch 物件。這通常很直接,但若需要快速導引,請參閱該 patch 何處。
裝飾器 patch() 也可以在 with 陳述式中被用來作為情境管理器:
>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
... thing = ProductionClass()
... thing.method(1, 2, 3)
...
>>> mock_method.assert_called_once_with(1, 2, 3)
也有 patch.dict(),用於在測試範圍中設定 dictionary(字典)內的值,並在測試結束時將其恢復為原始狀態:
>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original
Mock 支援對 Python 的魔術方法的 mocking。最簡單使用魔術方法的方式是使用 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'
為了確保測試中的 mock 物件與它們要替換的物件具有相同的 api,你可以使用自動規格。自動規格(auto-speccing)可以透過 patch 的 autospec 引數或 create_autospec() 函式來完成。自動規格建立的 mock 物件與它們要替換的物件具有相同的屬性和方法,並且任何函式和方法(包括建構函式)都具有與真實物件相同的呼叫簽名(call signature)。
這可以確保如果使用方法錯誤,你的 mock 會跟實際程式碼以相同的方式失敗:
>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
... pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
...
TypeError: missing a required argument: 'b'
create_autospec() 也可以用在類別上,它複製了 __init__ 方法的簽名,它也可以用在可呼叫物件上,其複製了 __call__ 方法的簽名。
Mock 類別¶
Mock 是一個彈性的的 mock 物件,旨在代替程式碼中 stubs 和 test doubles (測試替身)的使用。Mock 是可呼叫的,並在你存取它們時將屬性建立為新的 mock [1]。存取相同的屬性將永遠回傳相同的 mock。Mock 記錄了你如何使用它們,允許你判定你的程式碼對 mock 的行為。
MagicMock 是 Mock 的子類別,其中所有魔術方法均已預先建立並可供使用。也有不可呼叫的變體,在你 mock 無法呼叫的物件時很有用:NonCallableMock 和 NonCallableMagicMock
patch() 裝飾器可以輕鬆地用 Mock 物件臨時替換特定模組中的類別。預設情況下,patch() 會為你建立一個 MagicMock。你可以使用 patch() 的 new_callable 引數指定 Mock 的替代類別。
- class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)¶
建立一個新的
Mock物件。Mock接受數個可選的引數來指定 Mock 物件的行為:spec:這可以是字串的 list(串列),也可以是充當 mock 物件規格的現有物件(類別或實例)。如果傳入一個物件,則透過對該物件呼叫 dir 來形成字串的串列(不包括不支援的魔術屬性和方法)。存取不在此串列中的任何屬性都會引發
AttributeError。如果 spec 是一個物件(而不是一個字串的串列),那麼
__class__會回傳 spec 物件的類別。這允許 mocks 通過isinstance()測試。spec_set:spec 的一個更嚴格的變體。如果使用 spec_set,在 mock 上嘗試 set 或取得不在傳遞給 spec_set 的物件上的屬性將會引發
AttributeError。side_effect:每當呼叫 Mock 時要呼叫的函式,參見
side_effect屬性,用於引發例外或動態變更回傳值。該函式使用與 mock 相同的引數呼叫,且除非它回傳DEFAULT,否則此函式的回傳值將用作回傳值。side_effect 也可以是一個例外的類別或實例。在這種情況下,當呼叫 mock 時,該例外將被引發。
如果 side_effect 是一個可疊代物件,那麼對 mock 的每次呼叫將回傳可疊代物件中的下一個值。
side_effect 可以透過將其設置為
None來清除。return_value:當呼叫 mock 時回傳的值。預設情況下,這是一個新的 Mock(在首次存取時建立)。參見
return_value屬性。unsafe:預設情況下,存取任何以 assert、assret、asert、aseert 或 assrt 開頭的屬性將引發
AttributeError。如果傳遞unsafe=True,將會允許存取這些屬性。在 3.5 版被加入.
wraps:被 mock 物件包裝的項目。如果 wraps 不是
None,那麼呼叫 Mock 將透過被包裝的物件(回傳真實結果)。存取 mock 的屬性將會回傳一個 Mock 物件,該物件包裝了被包裝物件的對應屬性(因此嘗試存取不存在的屬性將引發AttributeError)。如果 mock 已經明確設定了 return_value,那麼呼叫並不會被傳遞到被包裝的物件,而是回傳已設定好的 return_value。
name:如果 mock 有一個名稱,那麼它會被用於 mock 的 repr。這對於除錯很有用。此名稱將被傳播到子 mocks。
Mocks 還可以使用任意的關鍵字引數進行呼叫。這些關鍵字引數將在建立 mock 之後用於設定 mock 的屬性。欲知更多,請參見
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. Calls: [call(), call()].
在 3.6 版被加入.
- assert_called_with(*args, **kwargs)¶
這個方法是一個便利的方式,用來斷言最後一次呼叫是以特定方式進行的:
>>> mock = Mock() >>> mock.method(1, 2, 3, test='wow') <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_with(1, 2, 3, test='wow')
- assert_called_once_with(*args, **kwargs)¶
確認 mock 只被呼叫一次,且該次呼叫使用了指定的引數。
>>> mock = Mock(return_value=None) >>> mock('foo', bar='baz') >>> mock.assert_called_once_with('foo', bar='baz') >>> mock('other', bar='values') >>> mock.assert_called_once_with('other', bar='values') Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times. Calls: [call('foo', bar='baz'), call('other', bar='values')].
- assert_any_call(*args, **kwargs)¶
斷言 mock 已經被使用指定的引數呼叫。
這個斷言在 mock 曾經被呼叫過時通過,不同於
assert_called_with()和assert_called_once_with(),他們針對的是最近的一次的呼叫,而且對於assert_called_once_with(),最近一次的呼叫還必須也是唯一一次的呼叫。>>> mock = Mock(return_value=None) >>> mock(1, 2, arg='thing') >>> mock('some', 'thing', 'else') >>> mock.assert_any_call(1, 2, arg='thing')
- assert_has_calls(calls, any_order=False)¶
斷言 mock 已經使用指定的呼叫方式來呼叫。此斷言會檢查
mock_calls串列中的呼叫。如果 any_order 為 false,那麼這些呼叫必須按照順序。在指定的呼叫之前或之後可以有額外的呼叫。
如果 any_order 為 true,那麼這些呼叫可以以任何順序出現,但它們必須全部出現在
mock_calls中。>>> mock = Mock(return_value=None) >>> mock(1) >>> mock(2) >>> mock(3) >>> mock(4) >>> calls = [call(2), call(3)] >>> mock.assert_has_calls(calls) >>> calls = [call(4), call(2), call(3)] >>> mock.assert_has_calls(calls, any_order=True)
- assert_not_called()¶
斷言 mock 從未被呼叫。
>>> m = Mock() >>> m.hello.assert_not_called() >>> obj = m.hello() >>> m.hello.assert_not_called() Traceback (most recent call last): ... AssertionError: Expected 'hello' to not have been called. Called 1 times. Calls: [call()].
在 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
This can be useful where you want to make a series of assertions that reuse the same object.
return_value parameter when set to
Trueresetsreturn_value:>>> mock = Mock(return_value=5) >>> mock('hello') 5 >>> mock.reset_mock(return_value=True) >>> mock('hello') <Mock name='mock()' id='...'>
side_effect parameter when set to
Trueresetsside_effect:>>> mock = Mock(side_effect=ValueError) >>> mock('hello') Traceback (most recent call last): ... ValueError >>> mock.reset_mock(side_effect=True) >>> mock('hello') <Mock name='mock()' id='...'>
Note that
reset_mock()doesn't clear thereturn_value,side_effector any child attributes you have set using normal assignment by default.Child mocks are reset as well.
在 3.6 版的變更: reset_mock 函式新增了兩個僅限關鍵字引數 (keyword-only arguments)。
- mock_add_spec(spec, spec_set=False)¶
向 mock 增加一個規格 (spec)。spec 可以是一個物件或一個字串串列 (list of strings)。只有在 spec 上的屬性才能作為 mock 的屬性被取得。
如果 spec_set 為 true,那麼只能設定在規格中的屬性。
- attach_mock(mock, attribute)¶
將一個 mock 作為這個 Mock 的屬性附加,取代它的名稱和上代 (parent)。對附加的 mock 的呼叫將被記錄在這個 Mock 的
method_calls和mock_calls屬性中。
- configure_mock(**kwargs)¶
透過關鍵字引數在 mock 上設定屬性。
可以在使用 method(方法)呼叫時,使用標準點記法 (dot notation) 並將字典拆開,為 child mock 設定屬性、回傳值和 side effects:
>>> mock = Mock() >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock.configure_mock(**attrs) >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyError
同樣的事情可以在 mock 的建構函式呼叫中實現:
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock = Mock(some_attribute='eggs', **attrs) >>> mock.some_attribute 'eggs' >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyError
configure_mock()的存在是為了在 mock 被建立後更容易進行組態設定。
- __dir__()¶
Mock物件限制了dir(some_mock)僅顯示有用的結果。對於具有 spec 的 mock,這包含所有被允許的 mock 屬性。請參閱
FILTER_DIR以了解這種過濾行為的作用,以及如何關閉它。
- _get_child_mock(**kw)¶
建立為了得到屬性和回傳值的 child mock。預設情況下,child mock 將與其上代是相同的型別。Mock 的子類別可能會想要置換此行為,以自訂 child mock 的建立方式。
對於不可呼叫的 mock,將使用可呼叫的變體,而不是任何的自訂子類別。
- called¶
一個 boolean(布林),表述 mock 物件是否已經被呼叫:
>>> mock = Mock(return_value=None) >>> mock.called False >>> mock() >>> mock.called True
- call_count¶
一個整數,告訴你 mock 物件被呼叫的次數:
>>> mock = Mock(return_value=None) >>> mock.call_count 0 >>> mock() >>> mock() >>> mock.call_count 2
- return_value¶
設定此值以配置呼叫 mock 時回傳的值:
>>> mock = Mock() >>> mock.return_value = 'fish' >>> mock() 'fish'
預設的回傳值是一個 mock 物件,你也可以按照正常的方式配置它:
>>> mock = Mock() >>> mock.return_value.attribute = sentinel.Attribute >>> mock.return_value() <Mock name='mock()()' id='...'> >>> mock.return_value.assert_called_with()
return_value也可以在建構函式中設定:>>> mock = Mock(return_value=3) >>> mock.return_value 3 >>> mock() 3
- side_effect¶
這可以是一個在呼叫 mock 時要呼叫的函式、一個可疊代物件,或者要引發的例外(類別或實例)。
如果你傳遞一個函式,它將被呼叫,其引數與 mock 相同,且除非該函式回傳
DEFAULT單例 (singleton),否則對 mock 的呼叫將回傳函式回傳的任何值。如果函式回傳DEFAULT,那麼 mock 將回傳其正常的回傳值(從return_value得到)。如果你傳遞一個可疊代物件,它將被用於檢索一個疊代器,該疊代器必須在每次呼叫時產出 (yield) 一個值。這個值可以是要引發的例外實例,或者是對 mock 呼叫時要回傳的值(處理
DEFAULT的方式與函式的狀況相同)。以下是一個引發例外的 mock 的範例(用於測試 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)
使用可被呼叫物件的範例:
>>> 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 上次被呼叫時使用的引數。這將以元組的形式呈現:第一個成員 (member),其可以透過args屬性存取,是 mock 被呼叫時傳遞的所有位置引數(或一個空元組)。第二個成員,其可以透過kwargs屬性存取,是所有關鍵字引數(或一個空字典)。>>> mock = Mock(return_value=None) >>> print(mock.call_args) None >>> mock() >>> mock.call_args call() >>> mock.call_args == () True >>> mock(3, 4) >>> mock.call_args call(3, 4) >>> mock.call_args == ((3, 4),) True >>> mock.call_args.args (3, 4) >>> mock.call_args.kwargs {} >>> mock(3, 4, 5, key='fish', next='w00t!') >>> mock.call_args call(3, 4, 5, key='fish', next='w00t!') >>> mock.call_args.args (3, 4, 5) >>> mock.call_args.kwargs {'key': 'fish', 'next': 'w00t!'}
call_args,以及串列call_args_list、method_calls和mock_calls的成員都是call物件。這些都是元組,因此可以解包以取得各個引數並進行更複雜的斷言。參見 calls as tuples。在 3.8 版的變更: 新增
args與kwargs特性。
- call_args_list¶
這是按順序列出所有呼叫 mock 物件的串列(因此串列的長度表示它被呼叫的次數)。在任何呼叫發生之前,它會是一個空的串列。
call物件可用於方便地建構呼叫的串列,以便與call_args_list進行比較。>>> mock = Mock(return_value=None) >>> mock() >>> mock(3, 4) >>> mock(key='fish', next='w00t!') >>> mock.call_args_list [call(), call(3, 4), call(key='fish', next='w00t!')] >>> expected = [(), ((3, 4),), ({'key': 'fish', 'next': 'w00t!'},)] >>> mock.call_args_list == expected True
call_args_list的成員都是call物件。這些物件可以被拆包為元組,以取得各個引數。參見 calls as tuples。
- method_calls¶
除了追蹤對自身的呼叫之外,mock 還會追蹤對方法和屬性的呼叫,以及它們(這些方法和屬性)的方法和屬性:
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.property.method.attribute() <Mock name='mock.property.method.attribute()' id='...'> >>> mock.method_calls [call.method(), call.property.method.attribute()]
method_calls的成員都是call物件。這些物件可以拆包為元組,以取得各個引數。參見 calls as tuples。
- mock_calls¶
mock_calls記錄了 所有 對 mock 物件的呼叫,包含其方法、魔術方法以及回傳值 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物件。這些物件可以拆包為元組,以取得各個引數。參見 calls as tuples。備註
mock_calls記錄的方式意味著在進行巢狀呼叫時,上代 (ancestor) 呼叫的參數不會被記錄,因此在比較時它們將始終相等:>>> mock = MagicMock() >>> mock.top(a=3).bottom() <MagicMock name='mock.top().bottom()' id='...'> >>> mock.mock_calls [call.top(a=3), call.top().bottom()] >>> mock.mock_calls[-1] == call.top(a=-1).bottom() True
- __class__¶
通常,物件的
__class__屬性會回傳它的型別。但對於擁有spec的 mock 物件,__class__會回傳 spec 的類別。這允許 mock 物件透過對它們所替代或偽裝的物件進行的isinstance()測試:>>> mock = Mock(spec=3) >>> isinstance(mock, int) True
__class__可以被指定,這允許 mock 通過isinstance()檢查,而不需要強制使用 spec:>>> mock = Mock() >>> mock.__class__ = dict >>> isinstance(mock, dict) True
- class unittest.mock.NonCallableMock(spec=None, wraps=None, name=None, spec_set=None, **kwargs)¶
Mock的一個不可呼叫版本。建構函式參數的意義與Mock相同,其例外為 return_value 和 side_effect 在不可呼叫的 mock 上無意義。
使用類別或實例作為 spec 或 spec_set 的 mock 物件能夠通過 isinstance() 測試:
>>> mock = Mock(spec=SomeClass)
>>> isinstance(mock, SomeClass)
True
>>> mock = Mock(spec_set=SomeClass())
>>> isinstance(mock, SomeClass)
True
Mock 類別支援 mock 魔術方法。細節請參考魔術方法。
Mock類別和 patch() 裝飾器於組態時接受任意的關鍵字引數。對於 patch() 裝飾器,這些關鍵字會傳遞給正在建立 mock 的建構函式。這些關鍵字引數用於配置 mock 的屬性:
>>> m = MagicMock(attribute=3, other='fish')
>>> m.attribute
3
>>> m.other
'fish'
Child mock 的回傳值和 side effect 可以使用使用點記法進行設置。由於你無法直接在呼叫中使用帶有點 (.) 的名稱,因此你必須建立一個字典並使用 ** 解包:
>>> 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
在匹配對 mock 的呼叫時,使用 spec(或 spec_set)建立的可呼叫 mock 將會內省 (introspect) 規格物件的簽名 (signature)。因此,它可以匹配實際呼叫的引數,無論它們是按位置傳遞還是按名稱傳遞:
>>> 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(自動規格) 時,它還適用於 mock 物件的方法呼叫。
在 3.4 版的變更: 對於已經設置了規格(spec)和自動規格(autospec)的 mock 物件,新增簽名內省功能。
- class unittest.mock.PropertyMock(*args, **kwargs)¶
一個理應在類別上當成
property或其他 descriptor 的 mock。PropertyMock提供了__get__()和__set__()方法,因此你可以在它被提取時指定回傳值。從物件中提取
PropertyMock實例會不帶任何引數呼叫 mock。設定它則會用設定的值來呼叫 mock:>>> class Foo: ... @property ... def foo(self): ... return 'something' ... @foo.setter ... def foo(self, value): ... pass ... >>> with patch('__main__.Foo.foo', new_callable=PropertyMock) as mock_foo: ... mock_foo.return_value = 'mockity-mock' ... this_foo = Foo() ... print(this_foo.foo) ... this_foo.foo = 6 ... mockity-mock >>> mock_foo.mock_calls [call(), call(6)]
由於 mock 屬性的儲存方式,你無法直接將 PropertyMock 附加到 mock 物件。但是你可以將其附加到 mock 型別的物件:
>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> type(m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()
警示
If an AttributeError is raised by PropertyMock,
it will be interpreted as a missing descriptor and
__getattr__() will be called on the parent mock:
>>> m = MagicMock()
>>> no_attribute = PropertyMock(side_effect=AttributeError)
>>> type(m).my_property = no_attribute
>>> m.my_property
<MagicMock name='mock.my_property' id='140165240345424'>
詳情請見 __getattr__()。
- class unittest.mock.AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)¶
MagicMock的非同步版本。AsyncMock物件的表現將被視為非同步函式,並且呼叫的結果是一個可等待物件。>>> mock = AsyncMock() >>> inspect.iscoroutinefunction(mock) True >>> inspect.isawaitable(mock()) True
mock()的結果是一個非同步函式,在它被等待後將具有side_effect或return_value的結果:如果
side_effect是一個函式,非同步函式將回傳該函式的結果,如果
side_effect是一個例外,則非同步函式將引發該例外,如果
side_effect是一個可疊代物件,非同步函式將回傳可疊代物件的下一個值,但如果結果序列耗盡,將立即引發StopAsyncIteration,如果
side_effect沒有被定義,非同步函式將回傳由return_value定義的值,因此在預設情況下,非同步函式回傳一個新的AsyncMock物件。
將
Mock或MagicMock的 spec 設定為非同步函式將導致在呼叫後回傳一個協程物件。>>> async def async_func(): pass ... >>> mock = MagicMock(async_func) >>> mock <MagicMock spec='function' id='...'> >>> mock() <coroutine object AsyncMockMixin._mock_call at ...>
將
Mock、MagicMock或AsyncMock的 spec 設定為具有同步和非同步函式的類別,會自動檢測同步函式並將其設定為MagicMock(如果上代 mock 為AsyncMock或MagicMock)或Mock(如果上代 mock 為Mock)。所有非同步函式將被設定為AsyncMock。>>> class ExampleClass: ... def sync_foo(): ... pass ... async def async_foo(): ... pass ... >>> a_mock = AsyncMock(ExampleClass) >>> a_mock.sync_foo <MagicMock name='mock.sync_foo' id='...'> >>> a_mock.async_foo <AsyncMock name='mock.async_foo' id='...'> >>> mock = Mock(ExampleClass) >>> mock.sync_foo <Mock name='mock.sync_foo' id='...'> >>> mock.async_foo <AsyncMock name='mock.async_foo' id='...'>
在 3.8 版被加入.
- assert_awaited()¶
斷言 mock 至少被等待過一次。請注意這與物件是否被呼叫是分開的,
await關鍵字必須被使用:>>> mock = AsyncMock() >>> async def main(coroutine_mock): ... await coroutine_mock ... >>> coroutine_mock = mock() >>> mock.called True >>> mock.assert_awaited() Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited. >>> asyncio.run(main(coroutine_mock)) >>> mock.assert_awaited()
- assert_awaited_once()¶
斷言 mock 正好被等待了一次。
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.assert_awaited_once() >>> asyncio.run(main()) >>> mock.assert_awaited_once() Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited once. Awaited 2 times.
- assert_awaited_with(*args, **kwargs)¶
斷言最後一次等待使用了指定的引數。
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_with('foo', bar='bar') >>> mock.assert_awaited_with('other') Traceback (most recent call last): ... AssertionError: expected await not found. Expected: mock('other') Actual: mock('foo', bar='bar')
- assert_awaited_once_with(*args, **kwargs)¶
斷言 mock 只被等待了一次並使用了指定的引數。
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_once_with('foo', bar='bar') >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_once_with('foo', bar='bar') Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited once. Awaited 2 times.
- assert_any_await(*args, **kwargs)¶
斷言 mock 曾經被使用指定的引數等待過。
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> asyncio.run(main('hello')) >>> mock.assert_any_await('foo', bar='bar') >>> mock.assert_any_await('other') Traceback (most recent call last): ... AssertionError: mock('other') await not found
- assert_has_awaits(calls, any_order=False)¶
斷言 mock 已被使用指定的呼叫進行等待。
await_args_list串列將被檢查以確認等待的內容。如果 any_order 為 false,則等待必須按照順序。指定的等待之前或之後可以有額外的呼叫。
如果 any_order 為 true,則等待可以以任何順序出現,但它們必須全部出現在
await_args_list中。>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> calls = [call("foo"), call("bar")] >>> mock.assert_has_awaits(calls) Traceback (most recent call last): ... AssertionError: Awaits not found. Expected: [call('foo'), call('bar')] Actual: [] >>> asyncio.run(main('foo')) >>> asyncio.run(main('bar')) >>> mock.assert_has_awaits(calls)
- assert_not_awaited()¶
斷言 mock 從未被等待。
>>> mock = AsyncMock() >>> mock.assert_not_awaited()
- reset_mock(*args, **kwargs)¶
參見
Mock.reset_mock()。同時將await_count設定為 0,await_args設定為 None,並清除await_args_list。
- await_count¶
一個整數,用來記錄 mock 物件已被等待的次數。
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.await_count 1 >>> asyncio.run(main()) >>> mock.await_count 2
- await_args¶
這可能是
None(如果 mock 尚未被等待),或者是上次等待 mock 時使用的引數。與Mock.call_args的功能相同。>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args >>> asyncio.run(main('foo')) >>> mock.await_args call('foo') >>> asyncio.run(main('bar')) >>> mock.await_args call('bar')
- await_args_list¶
這是一個按照順序記錄 mock 物件所有等待的串列(因此串列的長度表示該物件已被等待的次數)。在進行任何等待之前,此串列為空。
>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args_list [] >>> asyncio.run(main('foo')) >>> mock.await_args_list [call('foo')] >>> asyncio.run(main('bar')) >>> mock.await_args_list [call('foo'), call('bar')]
- class unittest.mock.ThreadingMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, *, timeout=UNSET, **kwargs)¶
A version of
MagicMockfor multithreading tests. TheThreadingMockobject provides extra methods to wait for a call to be invoked, rather than assert on it immediately.The default timeout is specified by the
timeoutargument, or if unset by theThreadingMock.DEFAULT_TIMEOUTattribute, which defaults to blocking (None).You can configure the global default timeout by setting
ThreadingMock.DEFAULT_TIMEOUT.- wait_until_called(*, timeout=UNSET)¶
等待直到 mock 被呼叫。
If a timeout was passed at the creation of the mock or if a timeout argument is passed to this function, the function raises an
AssertionErrorif the call is not performed in time.>>> mock = ThreadingMock() >>> thread = threading.Thread(target=mock) >>> thread.start() >>> mock.wait_until_called(timeout=1) >>> thread.join()
- wait_until_any_call_with(*args, **kwargs)¶
等到直到 mock 被以特定引數呼叫。
If a timeout was passed at the creation of the mock the function raises an
AssertionErrorif the call is not performed in time.>>> mock = ThreadingMock() >>> thread = threading.Thread(target=mock, args=("arg1", "arg2",), kwargs={"arg": "thing"}) >>> thread.start() >>> mock.wait_until_any_call_with("arg1", "arg2", arg="thing") >>> thread.join()
- DEFAULT_TIMEOUT¶
Global default timeout in seconds to create instances of
ThreadingMock.
在 3.13 版被加入.
呼叫¶
Mock 物件可被呼叫。呼叫將回傳設定為 return_value 屬性的值。預設的回傳值是一個新的 Mock 物件;它會在第一次存取回傳值時(無論是顯式存取還是透過呼叫 Mock)被建立,但是這個回傳值會被儲存,之後每次都回傳同一個值。
對物件的呼叫會被記錄在如 call_args 和 call_args_list 等屬性中。
如果 side_effect 被設定,那麼在呼叫被記錄後它才會被呼叫,所以如果 side_effect 引發例外,呼叫仍然會被記錄。
呼叫 mock 時引發例外的最簡單方式是將 side_effect 設定為例外類別或實例:
>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]
如果 side_effect 是一個函式,則該函式回傳的東西就是對 mock 的呼叫所回傳的值。side_effect 函式會使用與 mock 相同的引數被呼叫。這讓你可以根據輸入動態地變更呼叫的回傳值:
>>> def side_effect(value):
... return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]
如果你希望 mock 仍然回傳預設的回傳值(一個新的 mock),或者是任何已設定的回傳值,有兩種方法可以實現。從 side_effect 內部回傳 return_value,或回傳 DEFAULT:
>>> m = MagicMock()
>>> def side_effect(*args, **kwargs):
... return m.return_value
...
>>> m.side_effect = side_effect
>>> m.return_value = 3
>>> m()
3
>>> def side_effect(*args, **kwargs):
... return DEFAULT
...
>>> m.side_effect = side_effect
>>> m()
3
要刪除 side_effect,並恢復預設行為,將 side_effect 設為 None:
>>> m = MagicMock(return_value=6)
>>> def side_effect(*args, **kwargs):
... return 3
...
>>> m.side_effect = side_effect
>>> m()
3
>>> m.side_effect = None
>>> m()
6
side_effect 也可以是任何可疊代的物件。對 mock 的重複呼叫將從可疊代物件中回傳值(直到疊代物件耗盡並引發 StopIteration 為止):
>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
...
StopIteration
如果可疊代物件中的任何成員是例外,則它們將被引發而不是被回傳:
>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
...
ValueError
>>> m()
66
刪除屬性¶
Mock 物件會在需要時建立屬性。這使得它們可以假裝成任何種類的物件。
你可能希望一個 mock 物件在 hasattr() 呼叫時回傳 False,或者在屬性被提取時引發 AttributeError。你可以透過將物件提供為 mock 的 spec 來實現這一點,但這並不總是那麼好用。
你可以透過刪除屬性來「阻擋」它們。一旦刪除,再次存取該屬性將會引發 AttributeError。
>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
...
AttributeError: f
Mock 名稱與名稱屬性¶
由於 "name" 是傳遞給 Mock 建構函式的引數,如果你想讓你的 mock 物件擁有 "name" 屬性,你不能在建立時直接傳遞它。有兩種替代方法。其中一個選擇是使用 configure_mock():
>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'
更簡單的方法是在 mock 建立後直接設定 "name" 屬性:
>>> mock = MagicMock()
>>> mock.name = "foo"
如同屬性一般附加 mock¶
當你將一個 mock 附加為另一個 mock 的屬性(或作為回傳值),它將成為該 mock 的「子代 (child)」。對子代的呼叫將被記錄在上代的 method_calls 和 mock_calls 屬性中。這對於配置子代並將它們附加到上代,或將 mock 附加到記錄所有對子代的呼叫的上代並允許你對 mock 間的呼叫順序進行斷言非常有用:
>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]
如果 mock 有 name 引數,則上述規則會有例外。這使你可以防止「親屬關係 (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 會自動被賦予名稱。若要將具有名稱的 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')]
Patchers¶
patch 裝飾器僅用於在裝飾的函式範圍內對物件進行 patch。它們會自動為你處理 patch 的中止,即使有異常被引發也是如此。所有這些函式也可以在 with 陳述式中使用,或者作為類別裝飾器使用。
patch¶
備註
關鍵是要在正確的命名空間進行 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 陳述式的內部,目標會被 patch 成一個新的物件。當函式或 with 陳述式結束時,patch 就會被解除。如果 new 被省略,則如果被 patch 的物件是非同步函式,目標會被替換為
AsyncMock,反之則替換為MagicMock。如果patch()做為裝飾器使用且省略了 new,則所建立的 mock 會作為額外的引數傳遞給被裝飾的函式。如果patch()作為情境管理器使用,則所建立的 mock 將由情境管理器回傳。target 應該是以
'package.module.ClassName'形式出現的字串。target 會被引入並用 new 物件替換指定的物件,因此 target 必須可從你呼叫patch()的環境中引入。target 在執行被裝飾的函式時被引入,而不是在裝飾器作用時 (decoration time)。spec 和 spec_set 關鍵字引數會傳遞給
MagicMock,如果 patch 正在為你建立一個。此外,你還可以傳遞
spec=True或spec_set=True,這將導致 patch 將被 mock 的物件作為 spec/spec_set 物件傳遞。new_callable 允許你指定一個不同的類別或可呼叫的物件,用於被呼叫並建立 new 物件。預設情況下,對於非同步函式使用
AsyncMock,而對於其他情況則使用MagicMock。spec 的一種更強大的形式是 autospec。如果你設定
autospec=True,則該 mock 將使用被替換物件的規格來建立。該 mock 的所有屬性也將具有被替換物件的對應屬性的規格。被 mock 的方法和函式將檢查其引數,如果呼叫時引數與規格不符(被使用錯誤的簽名 (signature) 呼叫),將引發TypeError。對於替換類別的 mock,它們的回傳值(即 'instance')將具有與類別相同的規格。請參閱create_autospec()函式和 Autospeccing(自動規格)。你可以用
autospec=some_object替代autospec=True,以使用任意物件作為規格,而不是被替換的物件。預設情況下,
patch()將無法取代不存在的屬性。如果你傳入create=True,且屬性不存在,則當被 patch 的函式被呼叫時,patch 將為你建立該屬性,並在被 patch 的函式結束後再次刪除它。這對於撰寫針對你的生產程式碼在執行環境建立的屬性的測試時非常有用。此功能預設為關閉,因為這可能會相當危險。開啟這個功能後,你可以對於實際上不存在的 API 撰寫會通過的測試!備註
在 3.5 版的變更: 如果你正在 patch 模組中的內建函式,那麼你不需要傳遞
create=True,它預設會被加入。patch 可以做為
TestCase類別的裝飾器使用。它透過裝飾類別中的每個測試方法來運作。當你的測試方法共享一組常見的 patch 時,這會減少繁冗的代碼。patch()透過搜尋以patch.TEST_PREFIX開頭的方法名來尋找測試。預設情況下會是'test',這與unittest尋找測試的方式相匹配。你可以通過設定patch.TEST_PREFIX來指定別的前綴。透過 with 陳述式,Patch 可以做為情境管理器使用。patch 適用於 with 陳述式之後的縮排區塊。如果你使用 "as",則被 patch 的物件將被綁定到 "as" 後面的名稱;如果
patch()正在為你建立一個 mock 物件,這會非常有用。patch()接受任意的關鍵字引數。如果被 patch 的物件是非同步的,這些將會被傳遞給AsyncMock,如果是同步的則會傳遞給MagicMock,或如果指定了 new_callable,則傳遞給它。patch.dict(...)、patch.multiple(...)和patch.object(...)可用於其餘的使用情境。
patch() 作為函式裝飾器,為你建立 mock 並將其傳遞給被裝飾的函式:
>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
... print(mock_class is SomeClass)
...
>>> function(None)
True
Patch 一個類別會以 MagicMock 實例取代該類別。如果該類別在被測試的程式碼中實例化,那麼它將是會被使用的 mock 的 return_value。
如果該類別被實例化多次,你可以使用 side_effect 來每次回傳一個新的 mock。 或者你可以將 return_value 設定成你想要的任何值。
若要配置被 patch 的類別的實例方法的回傳值,你必須在 return_value 上進行配置。例如:
>>> class Class:
... def method(self):
... pass
...
>>> with patch('__main__.Class') as MockClass:
... instance = MockClass.return_value
... instance.method.return_value = 'foo'
... assert Class() is instance
... assert Class().method() == 'foo'
...
如果你使用 spec 或 spec_set 且 patch() 正在取代一個類別,那麼被建立的 mock 的回傳值將具有相同的規格。:
>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()
當你想要為被建立的 mock 使用一個替代的類別取代預設的 MagicMock 時,new_callable 引數非常有用。例如,如果你想要一個 NonCallableMock 被使用:
>>> thing = object()
>>> with patch('__main__.thing', new_callable=NonCallableMock) as mock_thing:
... assert thing is mock_thing
... thing()
...
Traceback (most recent call last):
...
TypeError: 'NonCallableMock' object is not callable
另一個用法是用一個 io.StringIO 實例替換一個物件:
>>> from io import StringIO
>>> def foo():
... print('Something')
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
... foo()
... assert mock_stdout.getvalue() == 'Something\n'
...
>>> test()
當 patch() 為你建立 mock 時,通常你需要做的第一件事就是配置 mock。其中一些配置可以在對 patch 的呼叫中完成。你傳遞到呼叫中的任何關鍵字都將用於在被建立的 mock 上設定屬性:
>>> patcher = patch('__main__.thing', first='one', second='two')
>>> mock_thing = patcher.start()
>>> mock_thing.first
'one'
>>> mock_thing.second
'two'
除了被建立的 mock 上的屬性外,還可以配置 child mock 的 return_value 和 side_effect。它們在語法上不能直接作為關鍵字引數傳入,但是以它們作為鍵的字典仍然可以使用 ** 擴充為一個 patch() 呼叫:
>>> config = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> patcher = patch('__main__.thing', **config)
>>> mock_thing = patcher.start()
>>> mock_thing.method()
3
>>> mock_thing.other()
Traceback (most recent call last):
...
KeyError
預設情況下,嘗試 patch 模組中不存在的函式(或類別中的方法或屬性)將會失敗,並引發 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()
patch.object¶
- patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶
使用一個 mock 物件 patch 一個物件(目標)上的命名成員(屬性)。
patch.object()可以做為裝飾器、類別裝飾器或情境管理器使用。引數 new、spec、create、spec_set、autospec 和 new_callable 與在patch()中的引數具有相同的意義。與patch()一樣,patch.object()接受任意關鍵字引數來配置它所建立的 mock 物件。當作為類別裝飾器使用時,
patch.object()會遵循patch.TEST_PREFIX來選擇要包裝的方法。
你可以使用三個引數或兩個引數來呼叫 patch.object()。三個引數的形式接受要被 patch 的物件、屬性名稱和要替換掉屬性的物件。
當使用兩個引數的形式呼叫時,你會省略要替換的物件,一個 mock 會為你建立並將其作為額外的引數傳遞給被裝飾的函式:
>>> @patch.object(SomeClass, 'class_method')
... def test(mock_method):
... SomeClass.class_method(3)
... mock_method.assert_called_with(3)
...
>>> test()
spec、create 和 patch.object() 的其他引數與在 patch() 中的引數具有相同的意義。
patch.dict¶
- patch.dict(in_dict, values=(), clear=False, **kwargs)¶
Patch 字典或類字典的物件,並在測試後將字典回復到其原本的狀態,其中回復的字典是測試前字典的複本。
in_dict 可以是一個字典或一個類對映的容器。如果它是一個對映,那麼它至少必須支援取得、設定、刪除項目以及對鍵的疊代。
in_dict 也可以是指定字典名稱的字串,然後透過 import 來取得該字典。
values 可以是要設定的值的字典。values 也可以是
(key, value)對 (pairs) 的可疊代物件。如果 clear 為 true,則在設定新值之前字典將被清除。
也可以使用任意關鍵字引數呼叫
patch.dict()以在字典中設定值。在 3.8 版的變更:
patch.dict()現在在做為情境管理器使用時回傳被 patch 的字典。
patch.dict() 可以做為情境管理器、裝飾器或類別裝飾器使用:
>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
... assert foo == {'newkey': 'newvalue'}
...
>>> test()
>>> assert foo == {}
當作為類別裝飾器使用時,patch.dict() 會遵循 patch.TEST_PREFIX(預設為 'test')來選擇要包裝的方法:
>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
... def test_sample(self):
... self.assertEqual(os.environ['newkey'], 'newvalue')
如果你想在測試中使用不同的前綴,你可以透過設定 patch.TEST_PREFIX 來告知 patcher 使用不同的前綴。請參閱 TEST_PREFIX 以得知如何修改前綴的更多內容。
patch.dict() 可用於在字典中新增成員,或單純地讓測試更改字典,並確保在測試結束時將字典回復原狀。
>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
... assert foo == {'newkey': 'newvalue'}
... assert patched_foo == {'newkey': 'newvalue'}
... # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
... patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
... print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ
可以在 patch.dict() 呼叫中使用關鍵字來設定字典中的值:
>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
... import mymodule
... mymodule.function('some', 'args')
...
'fish'
patch.dict() 可以與實際上不是字典的類字典物件一起使用。最低限度它們必須支援項目的取得、設定、刪除以及疊代或隸屬資格檢測。這對應到魔術方法中的 __getitem__()、__setitem__()、__delitem__() 以及 __iter__() 或 __contains__()。
>>> class Container:
... def __init__(self):
... self.values = {}
... def __getitem__(self, name):
... return self.values[name]
...