url: /posts/985c18ca802f1b6da828b92e082b4d4e/
title: 為什麼你的FastAPI測試覆蓋率總是低得讓人想哭?
date: 2025-09-01T03:56:38+08:00
lastmod: 2025-09-01T03:56:38+08:00
author: cmdragon
summary:
FastAPI 開發中,測試環境和基礎框架的搭建至關重要。使用 pipenv 或 poetry 管理虛擬環境和依賴,項目結構包括應用入口、API 路由、數據模型、數據庫連接和配置文件。核心框架代碼涉及 SQLAlchemy 配置、Pydantic 配置管理和 FastAPI 入口。測試覆蓋率檢測工具配置包括 pytest、coverage.py 和 pytest-cov,編寫測試用例並生成覆蓋率報告。優化策略包括分支覆蓋率測試、異步任務覆蓋率和目標覆蓋率報告,確保代碼質量和可維護性。
categories:
- fastapi
tags:
- FastAPI
- 測試環境配置
- 依賴管理
- 測試覆蓋率
- pytest
- SQLAlchemy
- Pydantic
<img src="https://api2.cmdragon.cn/upload/cmder/20250304_012821924.jpg" title="cmdragon_cn.png" alt="cmdragon_cn.png"/>
掃描二維碼關注或者微信搜一搜:編程智域 前端至全棧交流與成長
發現1000+提升效率與開發的AI工具和實用程序:https://tools.cmdragon.cn/
一、測試環境配置與基礎框架搭建
在 FastAPI 開發中,完善的測試環境和基礎框架是保證代碼質量和可維護性的關鍵。以下是具體實現步驟:
1.1 環境配置與依賴管理
使用 pipenv 或 poetry 管理虛擬環境和依賴:
# 安裝 pipenv
pip install pipenv
# 創建虛擬環境並安裝依賴
pipenv install fastapi uvicorn pytest httpx pydantic==2.0.0 sqlalchemy==2.0.0
依賴説明:
fastapi: Web 框架核心uvicorn: ASGI 服務器pytest: 測試框架httpx: 測試 HTTP 請求pydantic: 數據驗證(v2.0 新特性支持嚴格類型校驗)sqlalchemy: ORM 工具
1.2 基礎框架結構
創建項目目錄結構:
project/
├── app/
│ ├── main.py # 應用入口
│ ├── routes/ # API 路由
│ ├── models/ # Pydantic 數據模型
│ ├── database.py # 數據庫連接
│ └── config.py # 配置文件
├── tests/
│ ├── conftest.py # 測試配置
│ └── test_api.py # API 測試用例
└── requirements.txt
1.3 核心框架代碼
database.py (SQLAlchemy 配置):
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# 依賴注入數據庫會話
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
config.py (Pydantic 配置管理):
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "FastAPI Demo"
debug_mode: bool = False
class Config:
env_file = ".env"
settings = Settings()
main.py (FastAPI 入口):
from fastapi import FastAPI, Depends
from .database import get_db
from .routes import items_router
from .config import settings
app = FastAPI(title=settings.app_name)
# 掛載路由
app.include_router(items_router, prefix="/items")
@app.get("/")
async def root():
return {"message": "Hello World"}
🔍 課後 Quiz 1
問題: 為什麼使用 yield 而不是 return 提供數據庫會話?
答案解析:
在 get_db 中使用 yield 實現依賴注入的生命週期管理:
yield前的代碼在請求開始時執行(創建會話)yield後的代碼在請求結束時執行(關閉會話)- 這種方式確保即使出現異常也能正確釋放資源
⚠️ 常見報錯解決方案 (1.X)
報錯: 422 Unprocessable Entity
原因: 請求體不符合 Pydantic 模型定義
解決方案:
- 檢查請求的 JSON 數據結構
-
驗證模型字段是否匹配,例如:
class Item(BaseModel): name: str # 要求必須字符串類型 price: float - 使用
curl -v查看詳細錯誤信息
預防建議:
- 為模型字段添加默認值,如
name: str = "default" - 使用
Union支持多類型,如price: Union[float, None] = None
二、測試覆蓋率檢測工具配置
測試覆蓋率是衡量代碼質量的核心指標。FastAPI 推薦使用:
- pytest:測試運行器
- coverage.py:覆蓋率檢測
- pytest-cov:集成插件
2.1 配置 pytest
tests/conftest.py (測試依賴注入):
import pytest
from httpx import AsyncClient
from app.main import app
@pytest.fixture
async def client():
async with AsyncClient(app=app, base_url="http://test") as ac:
yield ac
2.2 編寫測試用例
tests/test_api.py:
import pytest
# 測試 API 端點
@pytest.mark.asyncio
async def test_create_item(client):
response = await client.post(
"/items/",
json={"name": "Test Item", "price": 9.99} # 符合 Pydantic 模型
)
assert response.status_code == 200
assert response.json()["name"] == "Test Item"
# 測試無效數據
@pytest.mark.asyncio
async def test_invalid_item(client):
response = await client.post(
"/items/",
json={"price": "invalid"} # 缺少必要字段 name
)
assert response.status_code == 422 # 觸發 Pydantic 驗證錯誤
2.3 覆蓋率檢測配置
-
安裝依賴:
pipenv install coverage pytest-cov -
運行測試並生成報告:
pytest --cov=app --cov-report=html tests/ -
查看 HTML 報告:
open htmlcov/index.html
2.4 持續集成集成
在 .github/workflows/ci.yml 中配置:
name: CI Pipeline
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
- name: Install dependencies
run: pip install pipenv && pipenv install --dev
- name: Run tests
run: pytest --cov=app --cov-fail-under=80 # 要求覆蓋率≥80%
🔍 課後 Quiz 2
問題: 覆蓋率報告中 --cov-fail-under=80 參數的作用是什麼?
答案解析:
該參數設置最低覆蓋率閾值:
- 如果整體覆蓋率低於 80%,測試將失敗
- 防止未經充分測試的代碼合併到主分支
- 在 CI/CD 流程中強制質量門禁
⚠️ 常見報錯解決方案 (2.X)
報錯: ModuleNotFoundError: No module named 'app'
原因: 測試運行路徑錯誤
解決方案:
-
從項目根目錄運行測試:
cd /project && pytest -
在
pytest.ini中添加:[pytest] pythonpath = .預防建議:
- 使用
__init__.py將目錄轉為 Python 包 - 避免在測試中硬編碼絕對路徑
三、測試覆蓋率優化策略
3.1 分支覆蓋率測試
# 測試不同業務分支
@pytest.mark.parametrize("price, discount", [
(100, 10), # 正常折扣
(50, 0), # 無折扣
(30, -5) # 無效折扣
])
async def test_discount_logic(client, price, discount):
response = await client.post(
"/items/",
json={"name": "Test", "price": price, "discount": discount}
)
if discount < 0:
assert response.status_code == 400 # 驗證業務規則
else:
assert response.status_code == 200
3.2 異步任務覆蓋率
對於後台異步任務:
from fastapi import BackgroundTasks
async def notify_admins(email: str):
# 模擬發送郵件
print(f"Sending email to {email}")
@app.post("/report")
async def create_report(background_tasks: BackgroundTasks):
background_tasks.add_task(notify_admins, "admin@example.com")
return {"message": "Report scheduled"}
測試策略:
# Mock 後台任務
from unittest.mock import MagicMock
@pytest.mark.asyncio
async def test_background_task(client):
app.notify_admins = MagicMock() # 替換為 Mock 函數
response = await client.post("/report")
app.notify_admins.assert_called_once_with("admin@example.com")
3.3 目標覆蓋率報告
----------- coverage: platform linux -----------
Name Stmts Miss Cover
-----------------------------------------
app/__init__.py 0 0 100%
app/main.py 15 0 100%
app/routes.py 20 1 95% # 缺失分支
-----------------------------------------
TOTAL 35 1 97%
餘下文章內容請點擊跳轉至 個人博客頁面 或者 掃碼關注或者微信搜一搜:編程智域 前端至全棧交流與成長,閲讀完整的文章:為什麼你的FastAPI測試覆蓋率總是低得讓人想哭?
<details>
<summary>往期文章歸檔</summary>
- 如何讓FastAPI測試不再成為你的噩夢? - cmdragon's Blog
- FastAPI測試環境配置的秘訣,你真的掌握了嗎? - cmdragon's Blog
- 全鏈路追蹤如何讓FastAPI微服務架構的每個請求都無所遁形? - cmdragon's Blog
- 如何在API高併發中玩轉資源隔離與限流策略? - cmdragon's Blog
- 任務分片執行模式如何讓你的FastAPI性能飆升? - cmdragon's Blog
- 冷熱任務分離:是提升Web性能的終極秘籍還是技術噱頭? - cmdragon's Blog
- 如何讓FastAPI在百萬級任務處理中依然遊刃有餘? - cmdragon's Blog
- 如何讓FastAPI與消息隊列的聯姻既甜蜜又可靠? - cmdragon's Blog
- 如何在FastAPI中巧妙實現延遲隊列,讓任務乖乖等待? - cmdragon's Blog
- FastAPI的死信隊列處理機制:為何你的消息系統需要它? - cmdragon's Blog
- 如何讓FastAPI任務系統在失敗時自動告警並自我修復? - cmdragon's Blog
- 如何用Prometheus和FastAPI打造任務監控的“火眼金睛”? - cmdragon's Blog
- 如何用APScheduler和FastAPI打造永不宕機的分佈式定時任務系統? - cmdragon's Blog
- 如何在 FastAPI 中玩轉 APScheduler,讓任務定時自動執行? - cmdragon's Blog
- 定時任務系統如何讓你的Web應用自動完成那些煩人的重複工作? - cmdragon's Blog
- Celery任務監控的魔法背後藏着什麼秘密? - cmdragon's Blog
- 如何讓Celery任務像VIP客户一樣享受優先待遇? - cmdragon's Blog
- 如何讓你的FastAPI Celery Worker在壓力下優雅起舞? - cmdragon's Blog
- FastAPI與Celery的完美邂逅,如何讓異步任務飛起來? - cmdragon's Blog
- FastAPI消息持久化與ACK機制:如何確保你的任務永不迷路? - cmdragon's Blog
- FastAPI的BackgroundTasks如何玩轉生產者-消費者模式? - cmdragon's Blog
- BackgroundTasks 還是 RabbitMQ?你的異步任務到底該選誰? - cmdragon's Blog
- BackgroundTasks與Celery:誰才是異步任務的終極贏家? - cmdragon's Blog
- 如何在 FastAPI 中優雅處理後台任務異常並實現智能重試? - cmdragon's Blog
- BackgroundTasks 如何巧妙駕馭多任務併發? - cmdragon's Blog
- 如何讓FastAPI後台任務像多米諾骨牌一樣井然有序地執行? - cmdragon's Blog
- FastAPI後台任務:是時候讓你的代碼飛起來了嗎? - cmdragon's Blog
- FastAPI後台任務為何能讓郵件發送如此絲滑? - cmdragon's Blog
- FastAPI的請求-響應週期為何需要後台任務分離? - cmdragon's Blog
- 如何在FastAPI中讓後台任務既高效又不會讓你的應用崩潰? - cmdragon's Blog
- FastAPI後台任務:異步魔法還是同步噩夢? - cmdragon's Blog
- 如何在FastAPI中玩轉Schema版本管理和灰度發佈? - cmdragon's Blog
- FastAPI的查詢白名單和安全沙箱機制如何確保你的API堅不可摧? - cmdragon's Blog
- 如何在 FastAPI 中玩轉 GraphQL 性能監控與 APM 集成? - cmdragon's Blog
- 如何在 FastAPI 中玩轉 GraphQL 和 WebSocket 的實時數據推送魔法? - cmdragon's Blog
- 如何在FastAPI中玩轉GraphQL聯邦架構,讓數據源手拉手跳探戈? - cmdragon's Blog
- GraphQL批量查詢優化:DataLoader如何讓數據庫訪問速度飛起來? - cmdragon's Blog
</details>
<details>
<summary>免費好用的熱門在線工具</summary>
- ASCII字符畫生成器 - 應用商店 | By cmdragon
- JSON Web Tokens 工具 - 應用商店 | By cmdragon
- Bcrypt 密碼工具 - 應用商店 | By cmdragon
- GIF 合成器 - 應用商店 | By cmdragon
- GIF 分解器 - 應用商店 | By cmdragon
- 文本隱寫術 - 應用商店 | By cmdragon
- CMDragon 在線工具 - 高級AI工具箱與開發者套件 | 免費好用的在線工具
- 應用商店 - 發現1000+提升效率與開發的AI工具和實用程序 | 免費好用的在線工具
- CMDragon 更新日誌 - 最新更新、功能與改進 | 免費好用的在線工具
- 支持我們 - 成為贊助者 | 免費好用的在線工具
- AI文本生成圖像 - 應用商店 | 免費好用的在線工具
- 臨時郵箱 - 應用商店 | 免費好用的在線工具
- 二維碼解析器 - 應用商店 | 免費好用的在線工具
- 文本轉思維導圖 - 應用商店 | 免費好用的在線工具
- 正則表達式可視化工具 - 應用商店 | 免費好用的在線工具
- 文件隱寫工具 - 應用商店 | 免費好用的在線工具
- IPTV 頻道探索器 - 應用商店 | 免費好用的在線工具
- 快傳 - 應用商店 | 免費好用的在線工具
- 隨機抽獎工具 - 應用商店 | 免費好用的在線工具
- 動漫場景查找器 - 應用商店 | 免費好用的在線工具
- 時間工具箱 - 應用商店 | 免費好用的在線工具
- 網速測試 - 應用商店 | 免費好用的在線工具
- AI 智能摳圖工具 - 應用商店 | 免費好用的在線工具
- 背景替換工具 - 應用商店 | 免費好用的在線工具
- 藝術二維碼生成器 - 應用商店 | 免費好用的在線工具
- Open Graph 元標籤生成器 - 應用商店 | 免費好用的在線工具
- 圖像對比工具 - 應用商店 | 免費好用的在線工具
- 圖片壓縮專業版 - 應用商店 | 免費好用的在線工具
- 密碼生成器 - 應用商店 | 免費好用的在線工具
- SVG優化器 - 應用商店 | 免費好用的在線工具
- 調色板生成器 - 應用商店 | 免費好用的在線工具
- 在線節拍器 - 應用商店 | 免費好用的在線工具
- IP歸屬地查詢 - 應用商店 | 免費好用的在線工具
- CSS網格佈局生成器 - 應用商店 | 免費好用的在線工具
- 郵箱驗證工具 - 應用商店 | 免費好用的在線工具
- 書法練習字帖 - 應用商店 | 免費好用的在線工具
- 金融計算器套件 - 應用商店 | 免費好用的在線工具
- 中國親戚關係計算器 - 應用商店 | 免費好用的在線工具
- Protocol Buffer 工具箱 - 應用商店 | 免費好用的在線工具
- IP歸屬地查詢 - 應用商店 | 免費好用的在線工具
- 圖片無損放大 - 應用商店 | 免費好用的在線工具
- 文本比較工具 - 應用商店 | 免費好用的在線工具
- IP批量查詢工具 - 應用商店 | 免費好用的在線工具
- 域名查詢工具 - 應用商店 | 免費好用的在線工具
- DNS工具箱 - 應用商店 | 免費好用的在線工具
- 網站圖標生成器 - 應用商店 | 免費好用的在線工具
- XML Sitemap
</details>