1. 程式人生 > >python介面自動化測試 - mock模組基本使用介紹

python介面自動化測試 - mock模組基本使用介紹

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裝飾器

一共兩種格式

  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)

&n