前言
節選並翻譯自 Testing with Python (part 6): Fake it... - Bite code!
介紹
mockito
是一個 pytest
外掛,相比於 unitest.mock
,具有如下特點:
- 所有物件都自動為
autospec
,無需擔心模擬物件被錯誤的呼叫而不被發現 - 不使用字串引數作為模擬物件,可以減少拼寫錯誤
- 提供的返回值或丟擲錯誤更加冗長,但也更明確和具體
安裝
使用 pip
安裝即可:
pip install pytest-mockito
與 unitest.mock
的對比
比如對以下內容中的 main
方法測試,檔名為 my_life_work.py
:
def transform(param):
return param * 2
def check(param):
return "bad" not in param
def calculate(param):
return len(param)
def main(param, option):
if option:
param = transform(param)
if not check(param):
raise ValueError("Woops")
return calculate(param)
使用 unitest.mock
,寫法如下:
import pytest
from unittest.mock import patch
from my_life_work import main
@patch("my_life_work.transform")
@patch("my_life_work.check")
@patch("my_life_work.calculate")
def test_main(calculate, check, transform):
check.return_value = True
calculate.return_value = 5
assert main("param", False) == calculate.return_value
transform.assert_not_called()
check.assert_called_with("param")
calculate.assert_called_once_with("param")
transform.return_value = "paramparam"
calculate.return_value = 10
assert main("param", True) == calculate.return_value
transform.assert_called_with("param")
check.assert_called_with("paramparam")
calculate.assert_called_with("paramparam")
with pytest.raises(ValueError):
check.side_effect = ValueError
main("bad_param", False)
check.assert_called_with("param")
transform.assert_not_called()
calculate.assert_not_called()
再看下使用 mockito
的寫法:
import pytest
import my_life_work # 匯入以使用 mock
from my_life_work import main
def test_main(expect):
expect(my_life_work, times=1).check("param").thenReturn(True)
expect(my_life_work, times=1).calculate("param").thenReturn(5)
# '...' 表示任意的引數,這裡不期望 transform 會被呼叫
expect(my_life_work, times=0).transform(...)
assert main("param", False) == 5
expect(my_life_work, times=1).transform("param").thenReturn("paramparam")
expect(my_life_work, times=1).check("paramparam").thenReturn(True)
expect(my_life_work, times=1).calculate("paramparam").thenReturn(10)
assert main("param", True) == 10
expect(my_life_work, times=1).check("bad_param").thenReturn(False)
expect(my_life_work, times=0).transform(...)
expect(my_life_work, times=0).calculate(...)
with pytest.raises(ValueError):
main("bad_param", False)
在 mockito
中,原來 mock
的寫法
@patch("my_life_work.check")
...
check.return_value = True
check.assert_called_with("param")
寫法變為:
expect(my_life_work, times=1).check("param").thenReturn(True)
更清晰,也不容易出錯。
參考
- Testing with Python (part 6): Fake it... - Bite code!
- pytest-mockito · PyPI