python介面自動化測試 - mock模組基本使用介紹
阿新 • • 發佈:2020-01-26
mock簡介
- py3已將mock整合到unittest庫中
- 為的就是更好的進行單元測試
- 簡單理解,模擬介面返回引數
- 通俗易懂,直接修改介面返回引數的值
mock作用
解決依賴問題,達到解耦作用
當我們測試某個目標介面(模組)時,該介面依賴其他介面,當被依賴的介面未開發完成時,可以用mock模擬被依賴介面,完成目標介面的測試
模擬複雜業務的介面
當我們測試某個目標介面(模組),該介面依賴一個非常複雜的介面時,可以用mock來模擬這個複雜的業務介面;也解決介面依賴一樣的原理
單元測試
如果某個介面(模組)未開發完成時,又需要編寫測試用例,則可以通過mock模擬該介面(模組)進行測試
前後端聯調
前端開發的頁面需要根據後端返回的不同狀態碼展示不同的頁面,當後端介面未開發完成時,也可通過mock來模擬後端介面返回自己想要的資料
mock類解讀
class Mock(spec=None,side_effect=None,return_value=DEFFAULT,name=None)
- secp:定義mock物件的屬性值,可以是列表,字串,甚至一個物件或者例項
- side_effect:可以用來丟擲異常或者動態改變返回值,它必須是一個iterator(列表),它會覆蓋return_value
- return_value:定義mock方法的返回值,它可以是一個值,可以是一個物件(如果存在side_effect引數那這個就沒有用,也就是不能同時用)
- name:作為mock物件的一個標識,在print時可以看到
mock實際使用
一個未開發完成的功能如何測試?
1 def add(self, a, b): 2 """兩個數相加""" 3 pass 4 5 6 class TestSub(unittest.TestCase): 7 """測試兩個數相加用例""" 8 9 def test_sub(self): 10 # 建立一個mock物件 return_value代表mock一個數據 11 mock_add = mock.Mock(return_value=15) 12 # 將mock物件賦予給被測函式 13 add = mock_add 14 # 呼叫被測函式 15 result = add(5, 5) 16 # 斷言實際結果和預期結果 17 self.assertEqual(result, 15)
一個完成開發的功能如何測試?
1 class SubClass(object): 2 def add(self, a, b): 3 """兩個數相加""" 4 return a + b 5 6 class TestSub(unittest.TestCase): 7 """測試兩個數相加用例""" 8 9 def test_add2(self): 10 # 初始化被測函式類例項 11 sub = SubClass() 12 # 建立一個mock物件 return_value代表mock一個數據 13 # 傳遞side_effect關鍵字引數, 會覆蓋return_value引數值, 使用真實的add方法測試 14 sub.add = mock.Mock(return_value=15, side_effect=sub.add) 15 # 呼叫被測函式 16 result = sub.add(5, 5) 17 # 斷言實際結果和預期結果18 self.assertEqual(result, 10)
side_effect:這裡給的引數值是sub.add相當於add方法的地址,當我們呼叫add方法時就會呼叫真實的add方法
簡單理解成:傳遞了side_effect引數且值為被測函式地址時,mock不會起作用;兩者不可共存
另外,side_effect接受的是一個可迭代序列,當傳遞多個值時,每次呼叫mock時會返回不同的值;如下
1 mock_obj = mock.Mock(side_effect= [1,2,3]) 2 print(mock_obj()) 3 print(mock_obj()) 4 print(mock_obj()) 5 print(mock_obj()) 6 7 # 輸出 8 Traceback (most recent call last): 9 1 10 File "D:/MyThreading/mymock.py", line 37, in <module> 11 2 12 print(mock_obj()) 13 3 14 File "C:\Python36\lib\unittest\mock.py", line 939, in __call__ 15 return _mock_self._mock_call(*args, **kwargs) 16 File "C:\Python36\lib\unittest\mock.py", line 998, in _mock_call 17 result = next(effect) 18 StopIteration
存在依賴關係的功能如何測試?
1 # 支付類 2 class Payment: 3 4 def requestOutofSystem(self, card_num, amount): 5 ''' 6 請求第三方外部支付介面,並返回響應碼 7 :param card_num: 卡號 8 :param amount: 支付金額 9 :return: 返回狀態碼,200 代表支付成功,500 代表支付異常失敗 10 ''' 11 # 第三方支付介面請求地址(故意寫錯) 12 url = "http://third.payment.pay/" 13 # 請求引數 14 data = {"card_num": card_num, "amount": amount} 15 response = requests.post(url, data=data) 16 # 返回狀態碼 17 return response.status_code 18 19 def doPay(self, user_id, card_num, amount): 20 ''' 21 支付 22 :param userId: 使用者ID 23 :param card_num: 卡號 24 :param amount: 支付金額 25 :return: 26 ''' 27 try: 28 # 呼叫第三方支付介面請求進行真實扣款 29 resp = self.requestOutofSystem(card_num, amount) 30 print('呼叫第三方支付介面返回結果:', resp) 31 except TimeoutError: 32 # 如果超時就重新呼叫一次 33 print('重試一次') 34 resp = self.requestOutofSystem(card_num, amount) 35 36 if resp == 200: 37 # 返回第三方支付成功,則進行系統裡面的扣款並記錄支付記錄等操作 38 print("{0}支付{1}成功!!!進行扣款並記錄支付記錄".format(user_id, amount)) 39 return 'success' 40 41 elif resp == 500: 42 # 返回第三方支付失敗,則不進行扣款 43 print("{0}支付{1}失敗!!不進行扣款!!!".format(user_id, amount)) 44 return 'fail' 45 46 # 單元測試類 47 class payTest(unittest.TestCase): 48 49 def test_pay_success(self): 50 pay = Payment() 51 # 模擬第三方支付介面返回200 52 pay.requestOutofSystem = mock.Mock(return_value=200) 53 resp = pay.doPay(user_id=1, card_num='12345678', amount=100) 54 self.assertEqual('success', resp) 55 56 def test_pay_fail(self): 57 pay = Payment() 58 # 模擬第三方支付介面返回500 59 pay.requestOutofSystem = mock.Mock(return_value=500) 60 resp = pay.doPay(user_id=1, card_num='12345678', amount=100) 61 self.assertEqual('fail', resp) 62 63 def test_pay_time_success(self): 64 pay = Payment() 65 # 模擬第三方支付介面首次支付超時,重試第二次成功 66 pay.requestOutofSystem = mock.Mock(side_effect=[TimeoutError, 200]) 67 resp = pay.doPay(user_id=1, card_num='12345678', amount=100) 68 self.assertEqual('success', resp) 69 70 def test_pay_time_fail(self): 71 pay = Payment() 72 # 模擬第三方支付介面首次支付超時,重試第二次失敗 73 pay.requestOutofSystem = mock.Mock(side_effect=[TimeoutError, 500]) 74 resp = pay.doPay(user_id=1, card_num='12345678', amount=100) 75 self.assertEqual('fail', resp)
也許有小夥伴會問,第三方支付都不能用,我們的測試結果是否是有效的呢?
通常在測試一個模組的時候,是可以認為其他模組的功能是正常的,只針對目標模組進行測試是沒有任何問題的,所以說測試結果也是正確的
mock裝飾器
一共兩種格式
- @patch('module名字.方法名')
- @patch.object(類名, '方法名')
1 # 裝飾類演示 2 from mock import Mock, patch 3 4 5 # 單獨的相乘函式 6 def multiple(a, b): 7 return a * b 8 9 10 # 單獨的捕獲Exception函式 11 def is_error(): 12 try: 13 os.mkdir("11") 14 return False 15 except Exception as e: 16 return True 17 18 19 # 計算類,包含add方法 20 class calculator(object): 21 def add(self, a, b): 22 return a + b 23 24 25 # 裝飾類演示 - 單元測試類 26 class TestProducer(unittest.TestCase): 27 28 # case執行前 29 def setUp(self): 30 self.calculator = calculator() 31 32 # mock一個函式,注意也要指定module 33 @patch('mock_learn.multiple') 34 def test_multiple(self, mock_multiple): 35 mock_multiple.return_value = 3 36 self.assertEqual(multiple(8, 14), 3) 37 38 # mock一個類物件的方法 39 @patch.object(calculator, 'add') 40 def test_add(self, mock_add): 41 mock_add.return_value = 3 42 self.assertEqual(self.calculator.add(8, 14), 3) 43 44 # mock呼叫方法返回多個不同的值 45 @patch.object(calculator, 'add') 46 def test_effect(self, mock_add): 47 mock_add.side_effect = [1, 2, 3] 48 self.assertEqual(self.calculator.add(8, 14), 1) 49 self.assertEqual(self.calculator.add(8, 14), 2) 50 self.assertEqual(self.calculator.add(8, 14), 3) 51 52 # mock的函式丟擲Exception 53 @patch('os.mkdir') 54 def test_exception(self, mkdir): 55 mkdir.side_effect = Exception 56 self.assertEqual(is_error(), True) 57 58 # mock多個函式,注意函式呼叫順序 59 @patch.object(calculator, 'add') 60 @patch('mock_learn.multiple') 61 def test_more(self, mock_multiple, mock_add): 62 mock_add.return_value = 1 63 mock_multiple.return_value = 4 64 self.assertEqual(self.calculator.add(3, 3), 1) 65 self.assertEqual(multiple(3, 3), 4)
&n