guest@blog.cmj.tw: ~/posts $

Python Mock


[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):
	...