[Mock Object] 是一個在測試時候會使用到的技巧:透過 mock 可以建立一個假造的物件並進行操作, 避免因測試而造成對實際資料、服務造成影響。因為使用虛假的物件,因此在測試環間建置時也較於簡單。
Python - mock
在 python 中提供一個 mock 的測試物件,透過這個物件可以動態 patch:1) 函數與 2) 物件。 在很多時候建置一個測試環境是相對困難,透過 mock 可以將繁雜的測試環境簡單化並測試 API 行為, 減少因為函式庫資料或者網路因素造成的麻煩。可以用以下的方式來測試 mock:
import os
import mock
with mock.patch('os.mkdir'):
os.mkdir('abc')
在 mock.patch 的生命週期中,os.mkdir 的實作已經被替換掉成 mock object。更進階的則是換掉引用的函數 ,像是替換掉在 lib 下實作的 functionX:
from lib import functionX
import mock
with mock.patch('functionX'):
functionX('mock')
或者提換引用函數中的底層實作,像是在 functionX 的實作中使用到 lib2.objectX.functionY 的實作, 就可以用 mock 直接替換成另一個實作:
from lib import functionX
import mock
def functionMock(*args, **kwargs):
''' What's you want to do '''
pass
with mock.patch('lib2.objectX.functionY', return_value=functionMock):
functionX('mock')
Deep Dive
在寫 Python 的 unittest 遇到了一些問題,記錄一下要解決的方式:
mock method but without attribute
當想要 mock 某個物件的 method 時、又希望這個 method 不能存取其他屬性,像是
class Foo:
def run(self):
print("running...")
f = Foo()
f.run.__name__ # expected raise AttributeError
這時候不能單指用
spec
或者
side_effect
,反而要用 new_callable
重新定義這個
method:
def fn(*args, **kwargs):
return mock.Mock(spec=[], return_value='stop')
with mock.patch.object(Foo, 'run' new_callable=fn):
...