在實際產品開發過程當中,某個服務或前端依賴一個服務接口,該接口可能依賴多個底層服務或模塊,或第三方接口,好比說服務 A 依賴服務B,服務B又依賴服務 C,以下圖所示:
html
這種依賴的問題會致使本來的需求目的是要驗證服務A,但因爲所依賴的服務B或者服務C不穩定或者未開發完成,致使工做沒法正常開展。
前端
那做爲測試工程師,面對這樣的情形,咱們該怎麼辦呢?解決這類問題的核心的思路:引入依賴服務替身,更通俗的叫法,引入Mock服務。python
今天就結合unittest框架,給你們分享一些關於Mock的一些常見使用。git
可能還有些讀者以前並無接觸過Mock,不清楚Mock是個啥。github
Mock簡單來理解,就是在測試過程當中,對於某些不容易構造或者不容易獲取的對象,用一個虛擬的對象來建立以便測試。而這個虛擬的對象就是mock對象。mock對象就是真實對象在調試期間的代替品。數據庫
有時也將Mock服務稱之爲測試服務替身,或者測試服務檔板,下圖很形象的描述了Mock的做用。
後端
就Mock功能而言,自己適用場景較多,但在實際項目中,引入Mock經常使用來解決的幾類,歸納起來,主要有:網絡
1.先後端聯調框架
好比你是一個前端頁面開發,如今須要開發一個功能:
下一個訂單,支付頁面的接口,根據支付結果,支付成功,展現支付成功頁,支付失敗,展現支付失敗頁。要完成此功能,你須要調用後端的接口,根據返回給你的結果,來展現不一樣的頁面。此時後端接口還沒開發好,做爲一個前端開發總不能等別人開發好了,你再開發,那你只有加班的命了。爲了同步開發完成任務,此時,你能夠根據接口文檔的規定,把接口的地址和入參傳過去,而後本身mock接口的不一樣返回界面,來完成前端的開發任務。函數
2.單元測試
因爲單元測試僅針對當前單元進行測試,這就要求全部的內部或者外部依賴都應該是穩定的,採用mock的方法模擬跟本單元依賴的其餘單元,能夠將測試重點放在當前單元功能,排除外界因素干擾,提高測試精準度。
3.第三方接口依賴
在作接口自動化的時候,有時候須要調用第三方的接口,可是別人公司的接口服務不受你的控制,有可能別人提供的測試環境今天服務給你開着,別人就關掉了,給自動化接口測試帶來不少的麻煩,此時就能夠經過mock來模擬接口的返回數據,好比模擬各類第三方異常時的返回。
Mock雖然是做爲依賴服務的替身,但並不須要原本來本去構造實現一個完整的服務邏輯,好比如今有一個A服務依賴B服務,須要經過Mock來替換B服務(作一個假的B服務替身)。
那麼咱們作一個 Mock 服務其實就是作了一個簡單的服務 B,它不須要實現原有服務 B 負載的處理邏輯,只要能按服務A須要服務B返回的處理邏輯給出對應返回數據就能夠了。
目前常見服務或接口協議主要兩種,一種是RPC,另外一種是HTTP/HTTPS,mock原理都相似,要麼是修改原服務地址爲Mock服務地址,要麼是攔截原服務的請求Mock返回值,總之就是構造一個假的服務,替代原有服務。
若是你不想本身動手構建一套Mock解決方案,市面上也提供了不少現存的Mock方案。
經常使用的有:EasyMock
、Mockito
、WireMock
、JMockit
、Mock
、Moco
。
若是你團隊技術基礎相對比較薄弱,推薦你看看Moco
這個方案,官網以下:
https://github.com/dreamhead/moco/
接下來,重點介紹Python系下Mock方案的使用。
unittest.mock是一個用於在Python中進行單元測試的庫,顧名思義這個庫的主要功能是模擬一些東西。它的主要功能是使用mock對象替代掉指定的Python對象,以達到模擬對象的行爲。
須要注意的是在Python2.x版本中,Mock須要單獨安裝
pip install -U mock
從Python 3.3之後的版本mock已經合併到unittest模塊中了,是unittest單元測試的一部分,直接導入過來就行
from unittest import mock
官方文檔:
https://docs.python.org/dev/library/unittest.mock.html
unittest.mock模塊中最經常使用的是Mock類。
Mock類庫是一個專門用於在unittest過程當中製做(僞造)和修改(篡改)測試對象的類庫,避免這些對象在單元測試過程當中依賴外部資源(網絡資源,數據庫鏈接,其它服務以及耗時過長等)
案例:
以下場景:支付是一個獨立的接口,由其它開發提供,根據支付的接口返回狀態去顯示失敗,仍是成功,這個是你須要實現的功能,代碼存放在pay.py腳本中:
# !/usr/bin/python3 # -*- coding: utf-8 -*- # @Author : Mike Zhou # @Email : 公衆號:測試開發技術 # @File : pay.py def zhifu(): '''假設這裏是一個支付的功能,未開發完 支付成功返回:{"result": "success", "msg":"支付成功"} 支付失敗返回:{"result": "fail", "msg":"餘額不足"} ''' pass def zhifu_statues(): '''根據支付的結果success or fail,判斷跳轉到對應頁面''' result = zhifu() try: if result["result"] == "success": return "支付成功" elif result["result"] == "fail": return "支付失敗" else: return "未知錯誤異常" except: return "Error, 服務端返回異常!"
在zhifu_statues方法中,依賴了zhifu方法,但因爲zhifu支付方法的接口是由另一個同事開發,正常狀況下,你同事開發的進度你是沒法控制的,須要等他開發完了你才能進行聯調你所負責的zhifu_statues接口,所以咱們能夠經過引入Mock來解決這個問題。
引入mock後單元測試用例代碼
# !/usr/bin/python3 # -*- coding: utf-8 -*- # @Author : Mike Zhou # @Email : 公衆號:測試開發技術 import unittest from unittest import mock import pay class TestZhifuStatues(unittest.TestCase): '''單元測試用例''' def test_01(self): '''測試支付成功場景''' # mock一個支付成功的數據 pay.zhifu = mock.Mock(return_value={"result": "success", "msg":"支付成功"}) # 根據支付結果測試頁面跳轉 statues = pay.zhifu_statues() print(statues) self.assertEqual(statues, "支付成功") def test_02(self): '''測試支付失敗場景''' # mock一個支付失敗的數據 pay.zhifu = mock.Mock(return_value={"result": "fail", "msg": "餘額不足"}) # 根據支付結果測試頁面跳轉 statues = pay.zhifu_statues() print(statues) self.assertEqual(statues, "支付失敗") if __name__ == "__main__": unittest.main()
上述代碼引入Mock
後,咱們就能夠順利完成對支付成功和支付異常兩類場景的驗證工做。(實際你能夠補充更多)
mock
中還有另外一種實現方式,經過patch
裝飾器的使用,patch
做爲函數裝飾器,爲您建立模擬並將其傳遞到裝飾函數。
用mock.patch實現以下:
# !/usr/bin/python3 # -*- coding: utf-8 -*- # @Author : Mike Zhou # @Email : 公衆號:測試開發技術 import unittest from unittest import mock import pay class TestZhifuStatues(unittest.TestCase): '''單元測試用例''' @mock.patch("pay.zhifu") def test_001(self, mock_zhifu): '''測試支付成功場景''' # 方法一:mock一個支付成功的數據 # pay.zhifu = mock.Mock(return_value={"result": "success", "msg":"支付成功"}) # print(pay.zhifu()) # 方法二:mock.path裝飾器模擬返回結果 mock_zhifu.return_value = {"result": "success", "msg":"支付成功"} # # 根據支付結果測試頁面跳轉 statues = pay.zhifu_statues() print(statues) self.assertEqual(statues, "支付成功") @mock.patch("pay.zhifu") def test_002(self, mock_zhifu): '''測試支付失敗場景''' # mock一個支付失敗的數據 mock_zhifu.return_value = {"result": "fail", "msg": "餘額不足"} # 根據支付結果測試頁面跳轉 statues = pay.zhifu_statues() self.assertEqual(statues, "支付失敗") if __name__ == "__main__": unittest.main()
還有更多的使用技巧,篇符有限,今天就先分享到這,若是以爲有用,歡迎關注!