# pytest與flask ###### tags: `unit-test` `pytest` `flask` ## 序言 在之前的幾篇隨筆中,各位應該不難發現,在測試進行時,我們需要import被測試的module,畢竟要測試一個函式的功能,首先得先把他抓出來吧。 但如果今天被測試的對象是一個Flask的API呢? ```python= @app.route('/login', methods=['GET', 'POST']) def login(): # 簡單的登入實作,get時給登入頁的html,post時驗證輸入的帳號密碼 if request.method == 'POST': # 取得使用者輸入的帳號和密碼 username = request.form['username'] password = request.form['password'] # 檢查帳號和密碼是否正確 if check_credentials(username, password): g.current_user = 'admin' return "登入成功" else: return "登入失敗" else: # 顯示登入表單 return render_template('login.html') ``` 想像我們現在有個運行中的Flask,手動測試的話我們會使用postman或curl打API,測試不同的輸入/form/headers會不會對API輸出造成影響,那今天我們要寫一個這樣的測試是不是要寫一個script跑postman呢? ## Flask的test_client 當然不是,因為flask提供了一個test_client的類別,首先我們有一個已經建立起的Factory method: ```python= # factory.py from flask import Flask def create_app(): app = Flask(__name__) return app ``` 在main.py中,要連接這個factory method會這樣寫: ```python= from my_project import create_app app = create_app() if __name__ == '__main__': app.run('0.0.0.0', port=5000) ``` 而在測試的流程中,我們可以用同樣的方式,但換成test_client: ```python= import pytest from my_project import create_app @pytest.fixture() def app(): app = create_app() app.config.update({ "TESTING": True, }) # setup if you need, and yield the app, make test_client open during test yield app # clean up / reset resources here @pytest.fixture() def client(app): return app.test_client() ``` 注意這邊使用pytest的fixture,可以讓我們在撰寫測試時直接在測試中調用client來用: ```python= def test_request_example(client): response = client.get("/posts") assert b"<h2>Hello, World!</h2>" in response.data ``` 這樣的寫法就可以讓我們直接對api做測試,在流程上就簡化了許多。 ## 參數詳細解釋 test_client主要有幾個常用到的參數,都以dict的形式給予: * query_string : 當API有需要再uri後續在?後的參數時使用,例如/users?id=12321 * headers : 當call的API需要headers時使用,例如需要將認證token放置在headers中的API * data : 當需要上傳檔案時可以使用,會自動幫你把headers中的Content-type設定成multipart/form-data * json : 送json body用的,會自動把Content-type設定成application/json 更多參數設定可以參考 https://flask.palletsprojects.com/en/2.3.x/testing/ ## 其他進階問題 **Q. 為什麼要用yield?** **A.** 因為要讓整個測試過程停留在看得到flask的狀態,包含g/session/reqeust等等flask的參數都可以被測試看到,如果用return當然也可以過,但就只看得到回傳回來的Response object **Q. 我可不可以在測試中直接設定flask.g? 比如設定g.user="test"給某個測試用** **A.** 可以用比較不直覺的方式拿到,首先要理解要修改g或session要手動把app_context或request_context打開...(下略2000字,這之後會是一篇文,大概),但簡單來說就是這樣幹 ```python= @pytest.fixture() def pre_test_client(): app = create_app() # 打開 test_client with app.test_client() as test_client: # 打開 application context with app.app_context(): # 現在在測試以外的地方也可以操控g跟create_app了 yield test_client ``` ## 結語 不知不覺也拖稿了一陣子,但好歹是把基礎的pytest/flask相關的簡單用法講完了,希望各位經過以上的文章可以了解用法,並基於自己需要測試的flask api進行修改。 至於flask的app context跟request context是怎麼設計,為什麼我們只要打開就可以設定g就留給之後的文章進行說明吧。 老樣子希望各位的專案與測試都進行的順利,我們下次見。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up