官方文檔 : https://docs.python.org/dev/library/unittest.mock.html
Mock
Mock類庫是一個專門用于在unittest過程中制作(偽造)和修改(篡改)測試對象的類庫,制作和修改的目的是避免這些對象在單元測試過程中依賴外部資源(網(wǎng)絡資源,數(shù)據(jù)庫連接,其它服務以及耗時過長等).
安裝
Python 2.7中沒有集成mock庫,Python3中的unittest中集成了mock庫.
Python 2.7環(huán)境下pip安裝:
$ pip install mock
快速使用
>>> from mock import MagicMock #MagicMock為Mock的子類
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
#指定返回3
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')
#斷言輸入是否為3,4,5,key='value',否則報錯
示例
#module.py
class Count():
def add(self, a, b):
return a + b
測試用例:
from unittest import mock
import unittest
from module import Count
class MockDemo(unittest.TestCase):
def test_add(self):
count = Count()
count.add = mock.Mock(return_value=13, side_effect=count.add)
result = count.add(8, 8)
print(result)
count.add.assert_called_with(8, 8)
self.assertEqual(result, 16)
if __name__ == '__main__':
unittest.main()
count.add = mock.Mock(return_value=13, side_effect=count.add)
side_effect參數(shù)和return_value是相反的。它給mock分配了可替換的結果,覆蓋了return_value。簡單的說,一個模擬工廠調用將返回side_effect值,而不是return_value。
所以,設置side_effect參數(shù)為Count類add()方法,那么return_value的作用失效。
測試依賴
例如,我們要測試A模塊,然后A模塊依賴于B模塊的調用。但是,由于B模塊的改變,導致了A模塊返回結果的改變,從而使A模塊的測試用例失敗。其實,對于A模塊,以及A模塊的用例來說,并沒有變化,不應該失敗才對。
通過mock模擬掉影響A模塊的部分(B模塊)。至于mock掉的部分(B模塊)應該由其它用例來測試。
# function.py
def add_and_multiply(x, y):
addition = x + y
multiple = multiply(x, y)
return (addition, multiple)
def multiply(x, y):
return x * y
然后,針對 add_and_multiply()函數(shù)編寫測試用例。func_test.py
import unittest
import function
class MyTestCase(unittest.TestCase):
def test_add_and_multiply(self):
x = 3
y = 5
addition, multiple = function.add_and_multiply(x, y)
self.assertEqual(8, addition)
self.assertEqual(15, multiple)
if __name__ == "__main__":
unittest.main()
add_and_multiply()函數(shù)依賴了multiply()函數(shù)的返回值。如果這個時候修改multiply()函數(shù)的代碼。
def multiply(x, y):
return x * y + 3
python3 func_test.py
F
======================================================================
FAIL: test_add_and_multiply (main.MyTestCase)
Traceback (most recent call last):
File "fun_test.py", line 19, in test_add_and_multiply
self.assertEqual(15, multiple)
AssertionError: 15 != 18
Ran 1 test in 0.000s
FAILED (failures=1)
測試用例運行失敗了,然而,add_and_multiply()函數(shù)以及它的測試用例并沒有做任何修改,罪魁禍首是multiply()函數(shù)引起的,我們應該把 multiply()函數(shù)mock掉。
import unittest
from unittest.mock import patch
import function
class MyTestCase(unittest.TestCase):
@patch("function.multiply")
def test_add_and_multiply2(self, mock_multiply):
x = 3
y = 5
mock_multiply.return_value = 15
addition, multiple = function.add_and_multiply(x, y)
mock_multiply.assert_called_once_with(3, 5)
self.assertEqual(8, addition)
self.assertEqual(15, multiple)
if __name__ == "__main__":
unittest.main()
@patch("function.multiply")
patch()裝飾/上下文管理器可以很容易地模擬類或對象在模塊測試。在測試過程中,您指定的對象將被替換為一個模擬(或其他對象),并在測試結束時還原。
這里模擬function.py文件中multiply()函數(shù)。
def test_add_and_multiply2(self, mock_multiply):
在定義測試用例中,將mock的multiply()函數(shù)(對象)重命名為 mock_multiply對象。
mock_multiply.return_value = 15
設定mock_multiply對象的返回值為固定的15。
ock_multiply.assert_called_once_with(3, 5)
檢查ock_multiply方法的參數(shù)是否正確。