一. 環境設置
Flask 套件選擇和安裝
此次選擇使用的是 Flask-Testing,此套件將 Python 內建的 Unittest 進行封裝,相較於 pytest 優點在建立 create_app 的時候非常簡單易懂。
Flask-Testing — Flask-Testing 0.3 documentation
pip3 install Flask-Testing
此次架構配置
會在專案中建立新資料夾 tests,並將所有的單元測試 py 檔放置於此資料夾內,此次架構上會搭配 Flask 工廠模式使用。
有關於 flask 工廠模式的好處和使用方法,可以參考此篇:
【Flask 教學】實作 Flask Application Factories 工廠模式 | Max行銷誌
└── flask
├── app
│ ├── __init__.py
│ ├── config
│ ├── model
│ └── view
├── main.py
└── tests
└── test_auth.py
二. 進入主題 Flask 實作單元測試
實作步驟一. 配置 main.py
我們將使用 Flask 的 CLI (Command Line Interface — Flask Documentation (1.1.x) ),CLI 的好處是可以在終端機中運行設定好的 Flask 指令,如下範例程式將命名函式為 test,所以我們只要在終端機中,先 cd
到 main.py 的位置,再下指令 flask test
就會執行以下設定好的指令,開始運行所有測試囉!
main.py
from flask_migrate import Migrate, upgrade, migrate
from app import create_app, db
app = create_app('development')
migrates = Migrate(app=app, db=db)
@app.cli.command()
def test():
import unittest
import sys
tests = unittest.TestLoader().discover("tests")
result = unittest.TextTestRunner(verbosity=2).run(tests)
if result.errors or result.failures:
sys.exit(1)
實作步驟二. 配置 test 檔
只要將想要運行的 py 檔,放置於此 tests 文件中,待會運行指令時就會被運行到,以下我們將寫一個範例 test_auth.py 檔,此份將會測試使用者登入的 api 運行。
tests/test_auth.py
import unittest
from flask import url_for
from flask_testing import TestCase
from app import create_app, db
import json
class SettingBase(TestCase):
def create_app(self):
return create_app("testing")
# 在運行測試之前會先被執行
def setUp(self):
db.create_all()
self.username = "[email protected]"
self.passwords = "666666"
self.role = 0
# 在結束測試時會被執行
def tearDown(self):
db.session.remove()
db.drop_all()
# signup 是測試時很常會被用到的功能,所以寫成函式,可以重複利用
def signup(self):
response = self.client.post(url_for('api.users'),
follow_redirects=True,
json={
"username": self.username,
"password": self.passwords,
"role": self.role
})
return response
# 這邊繼承剛剛的寫的 SettingBase class,接下來會把測試都寫在這裡
class CheckUserAndLogin(SettingBase):
def test_signup(self):
response = self.signup()
self.assertEqual(response.status_code, 200)
def test_signup_400(self):
# 測試密碼少於六位數
self.passwords = '123'
response = self.signup()
self.assertEqual(response.status_code, 400)
def test_signup_422(self):
# 測試重複註冊
response = self.signup()
response = self.signup()
self.assertEqual(response.status_code, 422)
if __name__ == '__main__':
unittest.main()
解析 1. 實例化 Flask instance
我們使用 Flask 工廠模式,所以從 app/__init__.py
中 import create_app,所以只需要 return create_app("testing")
def create_app(self):
return create_app("testing")
如果不使用工廠模式的話,就需要實例化的 app = Flask(__name__)
,並設定 config 參數
def create_app(self):
app = Flask(__name__)
app.config['TESTING'] = True
return app
解析 2. 測試前後工作準備
此外我們在 SettingBase 的 class 中有額外設定 setUp() 和 tearDown() 方法,setUp()
代表在運行測試之前會先被執行,而 tearDown()
代表在結束測試時會被執行。
# 在運行測試之前會先被執行
def setUp(self):
db.create_all()
self.username = "[email protected]"
self.passwords = "666666"
self.role = 0
# 在結束測試時會被執行
def tearDown(self):
db.session.remove()
db.drop_all()
解析 3. 斷言 Assertion 的使用方法
- assertEqual(a, b):a == b
- assertNotEqual(a, b):a != b
- assertTrue(x):bool(x) is True
- assertFalse(x):bool(x) is False
- assertIs(a, b):a is b
- assertIsNot(a, b):a is not b
- assertIsNone(x):x is None
- assertIsNotNone(x):x is not None
- assertIn(a, b):a in b
- assertNotIn(a, b):a not in b
- response.data:看回傳「結果」
- response.status_code:想看回傳「狀態」
更多詳細 assertIs 指令可以參考此篇:unittest — 單元測試框架 — Python 3.8.5 說明文件
實作步驟三. 運行測試
因為我們有在 main.py 寫入 CLI,所以只要終端機與 /flask/main.py
同位置,只需要下指令 flask test
就會開始運行測試
@app.cli.command()
def test():
import unittest
import sys
tests = unittest.TestLoader().discover("tests")
result = unittest.TextTestRunner(verbosity=2).run(tests)
if result.errors or result.failures:
sys.exit(1)
運行結果如下:
test_signup (test_auth.CheckUserAndLogin) ... ok
test_signup_400 (test_auth.CheckUserAndLogin) ... ok
test_signup_422 (test_auth.CheckUserAndLogin) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.402s
OK
補充. 覆蓋率測試
在終端機輸入以下指令,和剛剛一樣他會先偵測 /tests
裡面的所有的 py 檔,然後開始運行每一份測試檔,並將報告結果存在 /tests/test.coveragerc
coverage run --rcfile= /tests/test.coveragerc -m unittest discover -s tests/
coverage report -m
coverage html --rcfile= /tests/test.coveragerc
運行結果
...
----------------------------------------------------------------------
Ran 3 tests in 0.465s
OK
Name Stmts Miss Cover Missing
-------------------------------------------------------------
flask/tests/test_auth.py 3 0 100%
-------------------------------------------------------------
TOTAL 3 0 100%
補充:單元測試 Unit Test 原則
以下原則參考:Test F.I.R.S.T | Hacker Noon
F.I.R.S.T 原則 (F.I.R.S.T Principles of Unit Testing)
1. Fast – 快速
2. Independent – 獨立,測試之間要相互獨立,如果互相依賴的話,一個測試失敗會影響其他測試也都失敗。
3. Repeatable – 可重複,要在任何環境都可重複執行。
4. Self-Validating – 顯示驗證結果,可從 report 直接了解失敗原因
5. Timely – 及時,最好是在寫程式之前先寫測試 (TDD 概念)
文章中有提到在考量 Independent 時,會遵循 3A rule:
1. Arrange:建立此測試案例需要的初始值,和思考好的命名和變數命稱來讓測試更容易理解。
2. Act:呼叫目標方法
3. Assert:驗證是否符合預期
最後~
▍回顧本篇 Flask Unittest 文章:
- 環境設置
- Flask 套件選擇和安裝
- 此次架構配置
- 進入主題 Flask 實作單元測試
- 實作步驟一. 配置 main.py
- 實作步驟二. 配置 test 檔
- 實作步驟三. 運行測試
- 補充. 覆蓋率測試
- 補充:單元測試 Unit Test 原則
更多 Flask 教學相關閱讀:
▍關於 Flask 教學系列目錄:
▍關於 Flask 部署相關文章:
- 【Flask 教學系列】實作 GCP 部署 Flask + Nginx + uWSGI
- 【Flask 教學系列】實作 Flask + GitHub Action CI/CD
- 第一集:實作 Dockerfile + flask 教學 (附GitHub完整程式)
- 第二集:實作 Dockerfile + nginx + ssl + flask 教學 (附GitHub完整程式)
- 第三集:實作 Docker-compose (Flask+Nginx+PostgreSQL)
▍其他 Flask 相關教學:
- 【Flask教學系列】Flask 為甚麼需要 WSGI 與 Nginx
- 【Flask教學系列】Flask-SQLAlchemy 資料庫連線&設定入門 (一)
- 【Flask教學系列】Flask-JWT-Extended 實作
- 【Flask教學系列】實作 Flask CORS
- 【Flask教學系列】實作 Flask CSRF Protection
有關 Max行銷誌的最新文章,都會發佈在 Max 的 Facebook 粉絲專頁,如果想看最新更新,還請您按讚或是追蹤唷!
在〈【Flask 教學】實作 Flask 單元測試 Unit Test〉中有 1 則留言
寫得不錯喔