單元測試,主要是爲了測試某個方法,或是某個代碼快,對於各種輸入的處理,輸出是否符合預期。但由於其他庫、或模塊的依賴,以至於很難獨立測試我們自己實現的邏輯代碼。
對此,引出 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
下面舉例說明,如果對單一的接口寫測試用例
二、舉例:用戶登錄
- 用戶登錄是個常見的功能接口,接口邏輯之外的部分基本同上,這裏省略不寫。用戶使用 name 和 password 進行登陸操作,服務器收到請求後,根據 name 從數據庫查詢 password ,一致則返回 200 OK,不一致返回 400 Bad Request,很簡單的實現,如下:
其中 UserDB 爲數據模塊中,從數據庫查詢用戶數據的類。這裏對於登錄邏輯的單元測試,只指測試該部分最小的代碼塊,對於代碼塊中引入的依賴,在測試時都認爲是正常的。例如,在測試 login() 的時候,我們認爲 UserDB 是正常的、可用的,至於 UserDB 的可靠性,需要 UserDB 模塊的單元測試來保障。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
對於待測試模塊內引入的依賴,採用 mock 的方式模擬。 - 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)
(完)