动态

详情 返回 返回

如何用FastAPI玩轉多模塊測試與異步任務,讓代碼不再“鬧脾氣”? - 动态 详情


url: /posts/ddbfa0447a5d0d6f9af12e7a6b206f70/
title: 如何用FastAPI玩轉多模塊測試與異步任務,讓代碼不再“鬧脾氣”?
date: 2025-09-10T06:22:28+08:00
lastmod: 2025-09-10T06:22:28+08:00
author: cmdragon

summary:
FastAPI框架在多模塊集成測試中,通過pytestfixture創建臨時數據庫,確保測試數據隔離,並使用unittest.mock模擬外部依賴,驗證模塊間交互。異步任務通過BackgroundTasks處理,定時任務則藉助APScheduler實現,測試時手動觸發任務以驗證邏輯。常見報錯如422 Validation Error503 ServiceError,可通過檢查請求字段類型和服務連接解決。運行環境需Python 3.10+,依賴庫包括fastapipytest等。

categories:

  • fastapi

tags:

  • FastAPI
  • 多模塊集成測試
  • 異步任務
  • 定時任務
  • pytest
  • APScheduler
  • 模擬依賴

<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. 測試數據庫隔離:使用pytestfixture創建臨時數據庫,避免污染生產數據。

    # conftest.py
    import pytest
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker
    
    @pytest.fixture(scope="module")
    def test_db():
        # 使用SQLite內存數據庫
        engine = create_engine("sqlite:///:memory:")
        SessionLocal = sessionmaker(bind=engine)
        # 創建所有表
        Base.metadata.create_all(bind=engine)
        yield SessionLocal()
        engine.dispose()
  2. 模擬外部依賴:使用unittest.mock替換第三方服務(如API調用)。

    # test_payment.py
    from unittest.mock import patch
    
    def test_process_payment(test_db):
        # 模擬支付網關響應
        with patch("services.payment_gateway.charge") as mock_charge:
            mock_charge.return_value = {"status": "success"}
            response = client.post("/payments", json={"amount": 100})
            assert response.status_code == 200

流程圖

graph LR
  A["啓動測試"] --> B["初始化臨時數據庫"]
  B --> C["注入模擬依賴"]
  C --> D["執行API請求"]
  D --> E["驗證響應&數據庫狀態"]
  E --> F["清理資源"]

二、異步任務實現

在Web應用中,耗時操作(如郵件發送、文件處理)和定時任務(如數據備份、報表生成)是常見需求。如果同步執行這些操作:

  1. 會阻塞請求處理線程
  2. 導致用户等待時間過長
  3. 降低系統整體吞吐量

FastAPI通過異步架構提供了兩種解決方案:

graph LR
A[客户端請求] --> B{FastAPI路由}
B -->|即時返回| C[同步響應]
B -->|後台執行| D[異步任務]
B -->|定時觸發| E[定時任務]

2.1 核心組件:BackgroundTasks

使用BackgroundTasks將耗時操作放入後台執行:

from fastapi import BackgroundTasks, FastAPI
from pydantic import BaseModel

app = FastAPI()


class UserRequest(BaseModel):
    email: str
    message: str


def send_email(email: str, message: str):
    # 模擬郵件發送耗時操作
    print(f"Sending email to {email}: {message}")
    # 實際項目中可使用smtplib或SendGrid庫


@app.post("/notify")
async def notify_user(
        request: UserRequest,
        background_tasks: BackgroundTasks
):
    # 添加後台任務(非阻塞)
    background_tasks.add_task(
        send_email,
        request.email,
        request.message
    )
    return {"status": "Notification queued"}

2.2 實現原理

  • 異步調度器:FastAPI內置線程池管理後台任務
  • 任務隔離:每個任務在獨立線程中執行
  • 自動清理:任務完成後釋放資源

三、定時任務實現

3.1 使用APScheduler

APScheduler提供多種觸發器類型:

from apscheduler.schedulers.background import BackgroundScheduler
from datetime import datetime

# 創建調度器實例
scheduler = BackgroundScheduler()


# 定時任務示例:每60秒執行
@scheduler.scheduled_job('interval', seconds=60)
def database_backup():
    print(f"{datetime.now()}: Database backup started")
    # 實際備份邏輯


# 在FastAPI啓動時初始化
@app.on_event("startup")
def init_scheduler():
    scheduler.start()


# 關閉時清理
@app.on_event("shutdown")
def shutdown_scheduler():
    scheduler.shutdown()

3.2 定時規則配置

觸發類型 代碼示例 説明
固定間隔 seconds=30 每30秒執行一次
特定時間 hour=3, minute=30 每天03:30執行
Cron表達式 cron='0 12 * * 1-5' 工作日中午12點執行
一次性任務 trigger='date' 指定具體時間執行

四、集成驗證與測試

4.1 異步任務測試策略

from fastapi.testclient import TestClient
from unittest.mock import patch

client = TestClient(app)


def test_notify_user():
    # 模擬後台任務函數
    with patch("main.send_email") as mock_send:
        # 發送請求
        response = client.post(
            "/notify",
            json={"email": "test@ex.com", "message": "Hello"}
        )

        # 驗證即時響應
        assert response.status_code == 200
        assert response.json() == {"status": "Notification queued"}

        # 驗證任務被調用
        mock_send.assert_called_once_with("test@ex.com", "Hello")

4.2 定時任務驗證技巧

# 手動觸發任務進行驗證
def test_scheduler_job():
    # 直接調用定時任務函數
    database_backup()

    # 驗證日誌輸出或狀態變更
    # (需在實際項目中添加檢測點)

五、最佳實踐指南

  1. 任務冪等性:確保相同任務重複執行不會產生副作用
  2. 異常處理:所有任務必須包含異常捕獲
def send_email(email, message):
    try:
    # 發送邏輯
    except Exception as e:
        logger.error(f"郵件發送失敗: {str(e)}")
  1. 資源限制:配置任務併發量防止系統過載
# 限制最大併發線程數
background_tasks.add_task(..., max_workers=5)
  1. 監控集成:添加Prometheus指標暴露
from prometheus_fastapi_instrumentator import Instrumentator


@app.on_event("startup")
def init_monitoring():
    Instrumentator().instrument(app).expose(app)

六、課後 Quiz

  1. Q: 當添加100個後台任務但線程池只有10個線程時會發生什麼?
    A: 任務進入隊列依次執行
    B: 第11個任務直接失敗
    C: 自動擴容線程池
    D: 阻塞主線程

    答案: A
    解析:FastAPI使用隊列管理超額任務,當線程池滿時新任務排隊等候

  2. Q: 如何確保定時任務在服務器重啓後不丟失?
    A: 使用數據庫持久化任務狀態
    B: 配置APScheduler的持久化存儲
    C: 每次啓動重新註冊任務
    D: 使用外部任務隊列如Celery

    答案: B
    解析:APScheduler支持SQLite/Redis等持久化存儲:

    scheduler = BackgroundScheduler(
        jobstores={'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')}
    )

七、常見報錯解決方案

7.1 報錯:RuntimeError: No active scheduler

  • 原因:在路由中直接訪問未初始化的scheduler
  • 修復:通過依賴注入獲取實例
def get_scheduler():
    return scheduler


@app.get("/jobs")
def list_jobs(sched: BackgroundScheduler = Depends(get_scheduler)):
    return [str(job) for job in sched.get_jobs()]

7.2 報錯:JobNotFound 定時任務消失

  • 場景:修改代碼後重啓服務導致任務丟失
  • 預防

    1. 啓用持久化存儲
    2. 添加任務前檢查是否已存在
    if not scheduler.get_job('backup_job'):
        scheduler.add_job(..., id='backup_job')

7.3 報錯:422 Validation Error

  • 高頻場景:任務參數類型錯誤
  • 調試步驟

    1. 使用Pydantic驗證輸入參數
    def send_email(email: EmailStr, message: str):
        # EmailStr會自動驗證郵箱格式
    1. 測試時強制觸發驗證
    from pydantic import ValidationError
    try:
        UserRequest(email="invalid", message="test")
    except ValidationError as e:
        print(e.json())

第三方依賴清單

requirements.txt中聲明:

fastapi==0.104.0
pydantic==2.6.1
apscheduler==3.10.1
pytest==7.4.0
requests==2.31.0
prometheus-fastapi-instrumentator==6.1.0

餘下文章內容請點擊跳轉至 個人博客頁面 或者 掃碼關注或者微信搜一搜:編程智域 前端至全棧交流與成長
,閲讀完整的文章:如何用FastAPI玩轉多模塊測試與異步任務,讓代碼不再“鬧脾氣”?

<details>
<summary>往期文章歸檔</summary>

  • 如何在FastAPI中玩轉“時光倒流”的數據庫事務回滾測試?
  • 如何在FastAPI中優雅地模擬多模塊集成測試? - cmdragon's Blog
  • 多環境配置切換機制能否讓開發與生產無縫銜接? - cmdragon's Blog
  • 如何在 FastAPI 中巧妙覆蓋依賴注入並攔截第三方服務調用? - cmdragon's Blog
  • 為什麼你的單元測試需要Mock數據庫才能飛起來? - 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微服務架構的每個請求都無所遁形? - 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

</details>

<details>
<summary>免費好用的熱門在線工具</summary>

  • 歌詞生成工具 - 應用商店 | By cmdragon
  • 網盤資源聚合搜索 - 應用商店 | By cmdragon
  • 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>

user avatar huaweichenai 头像 datadowell 头像 240cgxo4 头像 thinking80s 头像 cloud11y 头像
点赞 5 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.