mock介紹
mock允許用模擬對象替換系統中真實對象,並對它們已使用的方式進行斷言。
在進行單元測試的時候,會遇到以下問題:
•接口的依賴;
•外部接口調用;
•測試環境非常復雜。
且單元測試應該只針對當前單元進行測試, 所有的內部或外部的依賴應該是穩定的, 已經在別處進行測試過的.使用mock 就可以對外部依賴組件實現進行模擬並且替換掉, 從而使得單元測試將焦點只放在當前的單元功能。
以下一個簡單的示例:
調用mock並指定 reutrn_value 創建一個mock對象,然后mock 掉 say_hello 函數對象后,這個方法的一切動作,比如 print 就變得沒有意義了,因為最后這個方法只會做一件事就是返回我們為其指定的返回值。
from unittest.mock import Mock def say_hello(word): print(f"Hello {word}") say_hello("China") mock=Mock(return_value="PYTHON OH YEAH") say_hello=mock say_hello("China")
mock模塊的基本使用
return_vaule
mock 對象的 return_vaule 的作用:它將忽略 mock 對象的行為,指定其返回值。
modular.py
class Count(): def add(self): pass
mock_demo01.py
from unittest import mock import unittest from modular import Count # test Count class class TestCount(unittest.TestCase): def test_add(self): count = Count() count.add = mock.Mock(return_value=13) result = count.add(8,5) self.assertEqual(result,13) if __name__ == '__main__': unittest.main()
假設Count計算類沒有實現,原本add() 方法要實現兩數相加。但這個功能還沒有完成。這時就可以借助mock對其進行測試。
count = Count()
首先,調用被測試類Count() 。
count.add = mock.Mock(return_value=7)
通過Mock類模擬被調用的方法add()方法,return_value 定義add()方法的返回值。
result = count.add(2,5)
接下來,相當於在正常的調用add()方法,傳兩個參數2和5,然后會得到相加的結果7。然后,7的結果是我們在上一步就預先設定好的。
self.assertEqual(result,7)
最后,通過assertEqual()方法斷言,返回的結果是否是預期的結果7。
side_effect
mock 對象的side_effect 的作用:通過side_effect指定mock對象的副作用,這個副作用就是當你調用這個mock對象時會調用的函數,也可以選擇拋出一個異常,來對程序的錯誤狀態進行測試。
from unittest.mock import Mock
def say_hello(word):
print(f"Hello {word}")
mock=Mock()
#指定為函數
mock.side_effect = say_hello
mock('china')
#指定為異常
mock.side_effect = KeyError('This is b') #Exception("Raise Exception")
mock()
另外也可以通過為side_effect指定一個列表,這樣在每次調用時會依次返回,如下:
from unittest.mock import Mock mock=Mock(side_effect = [1, 2, 3]) print(mock()) print(mock()) print(mock())
patch裝飾器
它是一個裝飾器,需要把你想模擬的函數寫在里面,然后在后面的單元測試案例中為它賦一個具體實例,再用 return_value 來指定模擬的這個函數希望返回的結果就可以了,后面就是正常單元測試代碼。
@mock.pathc.object(類名,“類中函數名”)
from unittest import mock import unittest class Count(): def add(self): pass # test Count class class TestCount(unittest.TestCase): @mock.patch.object(Count, "add") def test_add(self, mock_add): mock_add. return_value = 13 result = mock_add() self.assertEqual(result,13) if __name__ == '__main__': unittest.main()
@mock.pathc(模塊名,“函數名”)
linux_tool.py
import re def send_shell_cmd(): return "Response from send_shell_cmd function" def check_cmd_response(): response = send_shell_cmd() print("response: {}".format(response)) return re.search(r"mock_send_shell_cmd", response)
測試代碼:
from unittest import TestCase, mock import linux_tool class TestLinuxTool(TestCase): def setUp(self): pass def tearDown(self): pass @mock.patch("linux_tool.send_shell_cmd") def test_check_cmd_response(self, mock_send_shell_cmd): mock_send_shell_cmd.return_value = "Response from emulated mock_send_shell_cmd function" status = linux_tool.check_cmd_response() print("check result: %s" % status) self.assertTrue(status)
如果 patch 多個外部函數,那么調用遵循自下而上的規則,比如:
@mock.patch("function_C") @mock.patch("function_B") @mock.patch("function_A") def test_check_cmd_response(self, mock_function_A, mock_function_B, mock_function_C): mock_function_A.return_value = "Function A return" mock_function_B.return_value = "Function B return" mock_function_C.return_value = "Function C return" self.assertTrue(re.search("A", mock_function_A())) self.assertTrue(re.search("B", mock_function_B())) self.assertTrue(re.search("C", mock_function_C()))
一個示例
Count類中add_and_multiply依賴multiply,由於multiply並沒有實現,這時候可以使用mock替換multiply:
from unittest import mock import unittest class Count(): def add_and_multiply(self,x, y): addition = x + y multiple = self.multiply(x, y) return (addition, multiple) def multiply(self,x, y): pass # test Count class class TestCount(unittest.TestCase): @mock.patch.object(Count, "multiply") def test_add(self, mock_multiply): mock_multiply. return_value = 40 count = Count() addition,multiple = count.add_and_multiply(5,8) self.assertEqual(addition,13) self.assertEqual(multiple, 40) if __name__ == '__main__': unittest.main()