Flask API 單元測試 unittest,mock && patch

單元測試,主要是爲了測試某個方法,或是某個代碼快,對於各種輸入的處理,輸出是否符合預期。但由於其他庫、或模塊的依賴,以至於很難獨立測試我們自己實現的邏輯代碼。

對此,引出 mock。

一、Flask

Flask是個輕量 API 框架,使用起來非常容易上手

# 安裝:pip install flask
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello Flask'

app.run(port=5000)

這樣,一個簡單的 server 就跑起來了,訪問 http:localhost:5000 便可以看到返回的數據:Hello Flask

下面舉例說明,如果對單一的接口寫測試用例

二、舉例:用戶登錄

  1. 用戶登錄是個常見的功能接口,接口邏輯之外的部分基本同上,這裏省略不寫。用戶使用 namepassword 進行登陸操作,服務器收到請求後,根據 name 從數據庫查詢 password ,一致則返回 200 OK,不一致返回 400 Bad Request,很簡單的實現,如下:
    from flask import request
    from app.model import UserDB
    
    @app.route('/login')
    def login():
        name = request.args.get('name')
        if not name:
            return 'name is required', 400
       
        password = request.args.get('password')
        if not password:
            return 'password is required', 400
      
        # 從數據庫獲取用戶數據
        user = UserDB.get_user(name)
        if user.get('password') == password:
            return 'OK', 200
        else:
            return 'password is wrong', 400
    
    其中 UserDB 爲數據模塊中,從數據庫查詢用戶數據的類。這裏對於登錄邏輯的單元測試,只指測試該部分最小的代碼塊,對於代碼塊中引入的依賴,在測試時都認爲是正常的。例如,在測試 login() 的時候,我們認爲 UserDB 是正常的、可用的,至於 UserDB 的可靠性,需要 UserDB 模塊的單元測試來保障。
    對於待測試模塊內引入的依賴,採用 mock 的方式模擬。
  2. Flask 的單元測試,先看代碼
    import unittest
    from unittest.mock import Mock
    from unittest.mock import patch
    # 該app爲創建的Flask實例
    from application import app
    
    from app.model import UserDB
    
    class LoginTestCase(unittest.TestCase):
      
        def setUp(self):
            # push一個上下文,便可以使用flask中的全局變量,如g
            app.app_context().push()
            app.testing = True
            # 測試用的http client
            self.client = app.test_client()
        
        def test_login_success(self):
            # 真實請求中的url,host和port可省略
            url = '/login?name=flask&password=flaskpassword'
            # 模擬的方法名稱,也可直接寫字符串: get_user
            func_name = UserDB.get_user.__name__
            # 模擬的方法,不管請求參數是什麼,都會返回return_value的值(Mock還有其他用法)
            mock_func = Mock(return_value={'name': 'flask', 'password': 'flaskpassword'})
            # patch意爲,當UserDB的get_user方法被調用時,用mock出來的func來處理
            # 而mock的func,不管請求參數,都會返回return_value
            # 故而,只要UserDB的get_user被調用,都會返回{'name': 'flask', 'password': 'flaskpassword'}
            # with,表示這種處理方式的作用範圍
            # 當在with的範圍之外時,調用UserDB的get_user不受mock影響,會正常調用
            with patch.object(UserDB, func_name, func):
                # response爲返回的響應
                response = self.client.get(url)
                # 因爲傳入的name和password,和UserDB的mock func返回的name和password相同
                # 所以,該請求會返回200
                # assertEqual意爲,認定返回碼與200相等,若不等則該用例不通過
                self.assertEqual(response.status_code, 200)
           
        def test_login_failed(self):
            # 測試傳入錯誤密碼的情況
            url = '/login?name=flask&password=wrongpassword'
            func_name = UserDB.get_user.__name__
            mock_func = Mock(return_value={'name': 'flask', 'password': 'flaskpassword'})
            with patch.object(UserDB, func_name, func):
                response = self.client.get(url)
                # 因爲傳入密碼錯誤,所以在此我們認定返回碼是400
                self.assertEqual(response.status_code, 400)
    
    此外,還可以對測試缺少參數,這裏不再贅述。這樣,便可對接口的各種情況進行測試了。

(完)

發佈了39 篇原創文章 · 獲贊 16 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章