python接口自動化測試 - mock模塊基本使用介紹

mock簡介

  • py3已將mock集成到unittest庫中
  • 爲的就是更好的進行單元測試
  • 簡單理解,模擬接口返回參數
  • 通俗易懂,直接修改接口返回參數的值

 

mock做用

解決依賴問題,達到解耦做用

當咱們測試某個目標接口(模塊)時,該接口依賴其餘接口,當被依賴的接口未開發完成時,能夠用mock模擬被依賴接口,完成目標接口的測試前端

 

模擬複雜業務的接口

當咱們測試某個目標接口(模塊),該接口依賴一個很是複雜的接口時,能夠用mock來模擬這個複雜的業務接口;也解決接口依賴同樣的原理後端

 

單元測試

若是某個接口(模塊)未開發完成時,又須要編寫測試用例,則能夠經過mock模擬該接口(模塊)進行測試ide

 

先後端聯調

前端開發的頁面須要根據後端返回的不一樣狀態碼展現不一樣的頁面,當後端接口未開發完成時,也可經過mock來模擬後端接口返回本身想要的數據函數

 

mock類解讀

class Mock(spec=None,side_effect=None,return_value=DEFFAULT,name=None) post

  • 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時會返回不一樣的值;以下url

 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)

也許有小夥伴會問,第三方支付都不能用,咱們的測試結果是不是有效的呢?spa

一般在測試一個模塊的時候,是能夠認爲其餘模塊的功能是正常的,只針對目標模塊進行測試是沒有任何問題的,因此說測試結果也是正確的code

 

mock裝飾器

一共兩種格式

  1.  @patch('module名字.方法名') 
  2.  @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)
相關文章
相關標籤/搜索