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