測試對于軟件開發過程至關重要,可確保代碼按預期運行且無缺陷。在Python中,pytest是一種流行的測試框架,與標準單元測試模塊相比,它具有多種優勢,標準單元測試模塊是內置的Python測試框架,并且是標準庫的一部分。 pytest 包括更簡單的語法、更好的輸出、強大的裝置和豐富的插件生態系統。本教程將指導您設置 flask 應用程序、集成 pytest 固定裝置以及使用 p
第 1 步 – 設置環境
Ubuntu 24.04 默認情況下附帶 Python 3。打開終端并運行 使用以下命令來仔細檢查 Python 3 安裝:
root@Ubuntu:~# python3 --versionPython 3.12.3
如果 Python 3 已經安裝在你的機器上,上面的命令將 返回 Python 3 安裝的當前版本。如果不是 安裝完畢后,您可以運行以下命令并獲取Python 3 安裝:
root@ubuntu:~# sudo apt install python3
接下來,您需要在系統上安裝 pip 軟件包安裝程序:
root@ubuntu:~# sudo apt install python3-pip
安裝 pip 后,讓我們安裝 Flask。
第 2 步 – 創建 Flask應用程序
讓我們從創建一個簡單的 Flask 應用程序開始。為您的項目創建一個新目錄并導航到其中:
root@ubuntu:~# mkdir flask_testing_approot@ubuntu:~# cd flask_testing_app
現在,讓我們創建并激活一個虛擬環境來管理依賴項:
root@ubuntu:~# python3 -m venv venvroot@ubuntu:~# source venv/bin/activate
使用以下命令安裝 Flask pip:
root@ubuntu:~# pip install Flask
現在,讓我們創建一個簡單的 Flask 應用程序。創建一個名為 app.py 的新文件并添加以下代碼:
from flask import Flask, jsonify app = Flask(__name__)@app.route('/')def home(): return jsonify(message="Hello, Flask!")@app.route('/about')def about(): return jsonify(message="This is the About page")@app.route('/multiply/<int:x>/<int:y>')def multiply(x, y): result = x * y return jsonify(result=result)if __name__ == '__main__': app.run(debug=True)
此應用程序有三個路由:
- /:返回一個簡單的“Hello, Flask!”
- /about:返回一個簡單的“這是關于頁面”消息。
- /multiply/
/ :將兩個整數相乘并返回result.
要運行應用程序,請執行以下命令命令:
root@ubuntu:~# flask run
output* Serving Flask app "app" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL C to quit)
從上面的輸出中,您可以注意到服務器正在 http://127.0.0.1 上運行并偵聽端口 5000。打開另一個Ubuntu控制臺并一一執行以下cURL命令:
- GET:curl http://127.0.0.1:5000/:5000/
- GET:卷曲http://127.0.0.1:5000/about:5000/約
- GET:卷曲http://127.0.0.1:5000/multiply/10/20:5000/乘/10/20
讓我們了解一下這些 GET 請求是什么做:
-
卷曲http://127.0.0.1:5000/::5000/: 這將向 Flask 應用程序的根路由(‘/’)發送 GET 請求。服務器響應一個包含消息“Hello, Flask!”的 JSON 對象,演示了我們的主路由的基本功能。
-
curl http://127.0.0.1:5000/about::5000/about: 這將向 /about 路由發送 GET 請求。服務器使用包含消息“這是關于頁面”的 JSON 對象進行響應。這表明我們的路線運行正常。
-
curl http://127.0.0.1:5000/multiply/10/20::5000/multiply/10/20: 這會向 /multiply 路由發送一個 GET 請求,其中包含兩個參數:10 和 20。服務器將這些參數相乘 數字并以包含結果 (200) 的 JSON 對象進行響應。 這說明我們的multiply路由可以正確處理URL 參數并執行計算。
這些 GET 請求允許我們與 Flask 交互 應用程序的 API 端點,檢索信息或觸發 在服務器上執行操作而不修改任何數據。它們對于 獲取數據、測試端點功能并驗證我們的 路由按預期響應。
讓我們看看其中的每個 GET 請求操作:
root@ubuntu:~# curl root@ubuntu:~# curl http://127.0.0.1:5000/:5000/
Output{"message":"Hello, Flask!"}
root@ubuntu: ?#卷曲root@ubuntu:~# curl http://127.0.0.1:5000/about:500 0/關于
Output{"message":"This is the About page"}
root@ubuntu:~# curl root@ubuntu:~# curl http://127.0.0.1:5000/multiply/10/20:5000/multiply/10/20
Output{"result":200}
步驟3 – 安裝 pytest 并編寫您的第一個測試
現在您已經有了一個基本的 Flask 應用程序,讓我們安裝 pytest 并編寫一些單元測試。
使用 pip 安裝 pytest:
root@ubuntu:~# pip install pytest
創建一個測試目錄來存儲您的測試files:
root@ubuntu:~# mkdir tests
現在,讓我們創建一個名為 test_app.py 的新文件并添加以下代碼:
# Import sys module for modifying Python's runtime environmentimport sys# Import os module for interacting with the operating systemimport os# Add the parent directory to sys.pathsys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))# Import the Flask app instance from the main app filefrom app import app # Import pytest for writing and running testsimport pytest@pytest.fixturedef client(): """A test client for the app.""" with app.test_client() as client: yield clientdef test_home(client): """Test the home route.""" response = client.get('/') assert response.status_code == 200 assert response.json == {"message": "Hello, Flask!"}def test_about(client): """Test the about route.""" response = client.get('/about') assert response.status_code == 200 assert response.json == {"message": "This is the About page"}def test_multiply(client): """Test the multiply route with valid input.""" response = client.get('/multiply/3/4') assert response.status_code == 200 assert response.json == {"result": 12}def test_multiply_invalid_input(client): """Test the multiply route with invalid input.""" response = client.get('/multiply/three/four') assert response.status_code == 404def test_non_existent_route(client): """Test for a non-existent route.""" response = client.get('/non-existent') assert response.status_code == 404
我們來分解一下這個測試中的功能文件:
-
@pytest.fixture def client(): 這是一個 pytest 夾具,為我們的 Flask 應用程序創建一個測試客戶端。它使用 app.test_client() 方法創建一個客戶端,該客戶端可以向我們的應用程序發送請求,而無需運行實際的服務器。 Yield 語句允許客戶端在測試中使用,然后在每次測試后正確關閉。
-
def test_home(client): 此函數測試我們應用程序的主路由 (/)。它發送 使用測試客戶端向路由發出 GET 請求,然后斷言 響應狀態代碼為 200(正常)并且 JSON 響應與 預期消息。
-
def test_about(client): 與test_home類似,該函數測試about路由(/about)。它檢查 200 狀態代碼并驗證 JSON 響應內容。
-
def test_multiply(client): 此函數使用有效輸入 (/multiply/3/4) 測試乘法路由。它檢查狀態代碼是否為 200 并且 JSON 響應是否包含正確的乘法結果。
-
def test_multiply_invalid_input(client): 此函數測試具有無效輸入的乘法路徑(乘法/三/四)。 它檢查狀態代碼是否為 404(未找到),這是 當路由無法匹配字符串輸入時的預期行為 必需的整數參數。
-
def test_non_existent_route(client): 該函數測試當訪問不存在的路由時應用程序的行為。它向 /non-existent 發送 GET 請求, 我們的 Flask 應用程序中沒有定義它。測試斷言 響應狀態代碼為 404(未找到),確保我們的應用程序正確 處理對未定義路由的請求。
這些測試涵蓋了 Flask 應用程序的基本功能,確保 每條路線都能正確響應有效輸入,并且乘法 路由適當地處理無效輸入。通過使用 pytest,我們可以輕松運行這些測試來驗證我們的應用程序是否按預期工作。
第 4 步 – 運行測試
要運行測試,請執行以下命令:
root@ubuntu:~# pytest
默認情況下,pytest發現過程將遞歸掃描當前文件夾及其子文件夾對于名稱以“test_”開頭或以“_test”結尾的文件。然后執行位于這些文件中的測試。您應該看到類似以下內容的輸出:
Outputplatform Linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0 rootdir: /home/user/flask_testing_app collected 5 items tests/test_app.py .... [100%]======================================================= 5 passed in 0.19s ========================================================
這表明所有測試均已成功通過。
第 5 步:在 pytest 中使用 Fixtures
夾具是用于提供數據或資源的函數 測試。它們可用于設置和拆除測試環境、加載 數據,或執行其他設置任務。在 pytest 中,裝置是使用 @pytest.fixture 裝飾器定義的。
以下是如何增強現有裝置。更新客戶端固定裝置以使用安裝和拆卸邏輯:
@pytest.fixturedef client(): """Set up a test client for the app with setup and teardown logic.""" print("nSetting up the test client") with app.test_client() as client: yield client # This is where the testing happens print("Tearing down the test client")def test_home(client): """Test the home route.""" response = client.get('/') assert response.status_code == 200 assert response.json == {"message": "Hello, Flask!"}def test_about(client): """Test the about route.""" response = client.get('/about') assert response.status_code == 200 assert response.json == {"message": "This is the About page"}def test_multiply(client): """Test the multiply route with valid input.""" response = client.get('/multiply/3/4') assert response.status_code == 200 assert response.json == {"result": 12}def test_multiply_invalid_input(client): """Test the multiply route with invalid input.""" response = client.get('/multiply/three/four') assert response.status_code == 404def test_non_existent_route(client): """Test for a non-existent route.""" response = client.get('/non-existent') assert response.status_code == 404
這個 setup 添加了打印語句來演示安裝和拆卸 測試輸出中的階段。這些可以替換為實際資源 如果需要的話,管理代碼。
讓我們嘗試再次運行測試:
root@ubuntu:~# pytest -vs
-v 標志增加了詳細程度,而 -s 標志允許打印語句將顯示在控制臺輸出中。
您應該看到以下內容輸出:
Outputplatform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0 rootdir: /home/user/flask_testing_app cachedir: .pytest_cache collected 5 items tests/test_app.py::test_home Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_about Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_multiply Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_multiply_invalid_input Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_non_existent_route Setting up the test client PASSED Tearing down the test client============================================ 5 passed in 0.35s =============================================
第 6 步:添加失敗測試用例
讓我們向現有測試文件添加一個失敗測試用例。修改 test_app.py 文件并在失敗的測試用例末尾添加以下函數以獲得不正確的結果:
def test_multiply_edge_cases(client): """Test the multiply route with edge cases to demonstrate failing tests.""" # Test with zero response = client.get('/multiply/0/5') assert response.status_code == 200 assert response.json == {"result": 0} # Test with large numbers (this might fail if not handled properly) response = client.get('/multiply/1000000/1000000') assert response.status_code == 200 assert response.json == {"result": 1000000000000} # Intentional failing test: incorrect result response = client.get('/multiply/2/3') assert response.status_code == 200 assert response.json == {"result": 7}, "This test should fail to demonstrate a failing case"
讓我們休息一下分析 test_multiply_edge_cases 函數并解釋每個部分的含義執行:
-
使用零進行測試:此測試檢查乘法函數是否正確處理 乘以零。我們期望相乘時結果為0 任意數為零。這是一個需要測試的重要邊緣情況,因為一些 實現可能存在零乘法問題。
-
大數測試:此測試驗證乘法函數是否可以處理大數 沒有溢出或精度問題。我們乘以二百萬 值,預計結果為一萬億。這項測試至關重要,因為 它檢查函數能力的上限。請注意,這 如果服務器的實現不能處理大量數據,則可能會失敗 正確地,這可能表明需要大量的庫或 不同的數據類型。
-
故意失敗測試:此測試被故意設置為失敗。它檢查 2 * 3 是否等于 7, 這是不正確的。該測試旨在演示失敗的測試如何 查看測試輸出。這有助于理解如何識別 并調試失敗的測試,這是測試驅動的一項基本技能 開發和調試過程。
通過包含這些邊緣情況和故意失敗,您可以 不僅測試多重路由的基本功能,還測試 極端條件下的行為及其錯誤報告 能力。這種測試方法有助于確保穩健性和 我們應用程序的可靠性。
讓我們嘗試再次運行測試:
root@ubuntu:~# pytest -vs
您應該看到以下輸出:
Outputplatform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0 rootdir: /home/user/flask_testing_app cachedir: .pytest_cache collected 6 items tests/test_app.py::test_home Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_about Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_multiply Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_multiply_invalid_input Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_non_existent_route Setting up the test client PASSED Tearing down the test client tests/test_app.py::test_multiply_edge_cases Setting up the test client FAILED Tearing down the test client================================================================= FAILURES ==================================================================_________________________________________________________ test_multiply_edge_cases __________________________________________________________ client = <FlaskClient <Flask 'app'>> def test_multiply_edge_cases(client): """Test the multiply route with edge cases to demonstrate failing tests.""" # Test with zero response = client.get('/multiply/0/5') assert response.status_code == 200 assert response.json == {"result": 0} # Test with large numbers (this might fail if not handled properly) response = client.get('/multiply/1000000/1000000') assert response.status_code == 200 assert response.json == {"result": 1000000000000} # Intentional failing test: incorrect result response = client.get('/multiply/2/3') assert response.status_code == 200> assert response.json == {"result": 7}, "This test should fail to demonstrate a failing case"E AssertionError: This test should fail to demonstrate a failing caseE assert {'result': 6} == {'result': 7}E E Differing items: E {'result': 6} != {'result': 7}E E Full diff: E {E - 'result': 7,... E E ...Full output truncated (4 lines hidden), use '-vv' to show tests/test_app.py:61: AssertionError========================================================== short test summary info ==========================================================FAILED tests/test_app.py::test_multiply_edge_cases - AssertionError: This test should fail to demonstrate a failing case======================================================== 1 failed, 5 passed in 0.32s ========================================================
上面的失敗消息表明測試 test_multiply_edge_cases 中測試/test_app.py 文件失敗。具體來說,此測試函數中的最后一個斷言導致了失敗。
這種故意失敗對于演示如何測試非常有用 報告故障以及故障中提供了哪些信息 信息。它顯示了發生故障的確切行, 預期值和實際值,以及兩者之間的差異。
在現實場景中,您將修復代碼以進行測試 如果預期結果不正確,則通過或調整測試。然而, 在這種情況下,失敗是出于教育目的而故意發生的。