測試開發工程必備技能之一:Mock的使用

1. 背景

在實際產品開發過程當中,某個服務或前端依賴一個服務接口,該接口可能依賴多個底層服務或模塊,或第三方接口,好比說服務 A 依賴服務B,服務B又依賴服務 C,以下圖所示:
html

這種依賴的問題會致使本來的需求目的是要驗證服務A,但因爲所依賴的服務B或者服務C不穩定或者未開發完成,致使工做沒法正常開展。
前端

那做爲測試工程師,面對這樣的情形,咱們該怎麼辦呢?解決這類問題的核心的思路:引入依賴服務替身,更通俗的叫法,引入Mock服務。python

今天就結合unittest框架,給你們分享一些關於Mock的一些常見使用。git

2. Mock是什麼

可能還有些讀者以前並無接觸過Mock,不清楚Mock是個啥。github

Mock簡單來理解,就是在測試過程當中,對於某些不容易構造或者不容易獲取的對象,用一個虛擬的對象來建立以便測試。而這個虛擬的對象就是mock對象。mock對象就是真實對象在調試期間的代替品。數據庫

有時也將Mock服務稱之爲測試服務替身,或者測試服務檔板,下圖很形象的描述了Mock的做用。
後端

3. Mock能作什麼

就Mock功能而言,自己適用場景較多,但在實際項目中,引入Mock經常使用來解決的幾類,歸納起來,主要有:網絡

  • 接口間的相互依賴
  • 單元測試
  • 第三方接口調用

1.先後端聯調框架

好比你是一個前端頁面開發,如今須要開發一個功能:
下一個訂單,支付頁面的接口,根據支付結果,支付成功,展現支付成功頁,支付失敗,展現支付失敗頁。要完成此功能,你須要調用後端的接口,根據返回給你的結果,來展現不一樣的頁面。此時後端接口還沒開發好,做爲一個前端開發總不能等別人開發好了,你再開發,那你只有加班的命了。爲了同步開發完成任務,此時,你能夠根據接口文檔的規定,把接口的地址和入參傳過去,而後本身mock接口的不一樣返回界面,來完成前端的開發任務。函數

2.單元測試

因爲單元測試僅針對當前單元進行測試,這就要求全部的內部或者外部依賴都應該是穩定的,採用mock的方法模擬跟本單元依賴的其餘單元,能夠將測試重點放在當前單元功能,排除外界因素干擾,提高測試精準度。

3.第三方接口依賴

在作接口自動化的時候,有時候須要調用第三方的接口,可是別人公司的接口服務不受你的控制,有可能別人提供的測試環境今天服務給你開着,別人就關掉了,給自動化接口測試帶來不少的麻煩,此時就能夠經過mock來模擬接口的返回數據,好比模擬各類第三方異常時的返回。

4. Mock實現方式

Mock雖然是做爲依賴服務的替身,但並不須要原本來本去構造實現一個完整的服務邏輯,好比如今有一個A服務依賴B服務,須要經過Mock來替換B服務(作一個假的B服務替身)。

那麼咱們作一個 Mock 服務其實就是作了一個簡單的服務 B,它不須要實現原有服務 B 負載的處理邏輯,只要能按服務A須要服務B返回的處理邏輯給出對應返回數據就能夠了。

目前常見服務或接口協議主要兩種,一種是RPC,另外一種是HTTP/HTTPS,mock原理都相似,要麼是修改原服務地址爲Mock服務地址,要麼是攔截原服務的請求Mock返回值,總之就是構造一個假的服務,替代原有服務。

5. Mock市面上常見的解決方案

若是你不想本身動手構建一套Mock解決方案,市面上也提供了不少現存的Mock方案。
經常使用的有:EasyMockMockitoWireMockJMockitMockMoco

若是你團隊技術基礎相對比較薄弱,推薦你看看Moco這個方案,官網以下:

https://github.com/dreamhead/moco/

接下來,重點介紹Python系下Mock方案的使用。

6. Python下unittest.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()

還有更多的使用技巧,篇符有限,今天就先分享到這,若是以爲有用,歡迎關注!

相關文章
相關標籤/搜索