惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
Blog — PlanetScale
Blog — PlanetScale
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
爱范儿
爱范儿
量子位
博客园 - 聂微东
Engineering at Meta
Engineering at Meta
WordPress大学
WordPress大学
GbyAI
GbyAI
MyScale Blog
MyScale Blog
IT之家
IT之家
P
Proofpoint News Feed
M
MIT News - Artificial intelligence
The Cloudflare Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Hugging Face - Blog
Hugging Face - Blog
The Register - Security
The Register - Security
Microsoft Security Blog
Microsoft Security Blog
博客园_首页
MongoDB | Blog
MongoDB | Blog
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
Y
Y Combinator Blog
雷峰网
雷峰网
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Last Week in AI
Last Week in AI
博客园 - 叶小钗
D
DataBreaches.Net
B
Blog
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
aimingoo的专栏
aimingoo的专栏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The GitHub Blog
The GitHub Blog
云风的 BLOG
云风的 BLOG
Recent Announcements
Recent Announcements
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
腾讯CDC
T
Threat Research - Cisco Blogs
SecWiki News
SecWiki News
Martin Fowler
Martin Fowler
D
Docker
Cisco Talos Blog
Cisco Talos Blog
T
Tenable Blog
Webroot Blog
Webroot Blog
宝玉的分享
宝玉的分享

博客园 - 一名程序媛呀

Anki插件开发必知必会:钩子函数与右键菜单定制 httpx 传参总报错?这次把 GET、POST、文件上传到响应处理的坑给你一次填平 从卡顿到丝滑:FastAPI 调用外部 API 的正确姿势(httpx 实战) 还在 XHR、Fetch 和 Axios 之间纠结?我踩过的坑,希望你一个都不用碰到 你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南 Uvicorn、Gunicorn 傻傻分不清?FastAPI 生产部署避坑指南 Termux里的二进制和脚本,到底怎么运行才不踩坑?Termux-service 保活妙招! 刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API 别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了 掏出手机就能搭个 WebDAV 同步服务器?这操作有点香 别只盯着GitBook了!这个文档神器让你的笔记秒变网站 写爬虫时用了代理还被封?Python 代理的那些隐藏坑,我替你踩明白了 FastAPI 身份验证总踩坑?这份 FastAPI Users “避坑指南”请收好 旧手机别扔!用 Termux 搭个私人云盘,比网盘香多了 你的FastAPI又在服务器上“跑不起来”了?来,今天咱把打包这件事彻底聊透 写页面时别再把 Element Plus 整个搬进来啦!Vue3按需加载的坑我帮你踩平了 前端包管理咋选?我从npm叛逃到pnpm的血泪史(附避坑指南) 聊聊 fetch 使用中我踩过的那些坑和正确打开方式 FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示? 给 FastApiAdmin 加个“会议纪要”模块,我把后端二次开发的坑踩了个遍 我用了FastApiAdmin后,连夜把踩过的坑都整理出来了 告别 Typora 后的新欢:我把所有笔记迁移到了 Obsidian 这个“第二大脑” 你的Agent API还在裸奔?从认证到沙箱,我用FastAPI搭了几道防线 让 FastAPI Agent 思考不阻塞:手把手教你实现异步任务与后台处理方案 让FastAPI Agent真正记住你:聊聊会话记忆与持久化存储的落地实践 FastAPI Agent 函数调用实战:我让 AI 学会了“自己动手查天气“ 初探:用 FastAPI 搭建你的第一个 AI Agent 接口 FastAPI 少有人提的实用技巧:把 Depends 依赖提到路由层,代码少写60% FastAPI 生产环境静态文件完全指南:从 /favicon.ico 404 到 HSTS 混合内容,一次全根治 用了loguru我才明白,Python日志还能这么写 FastAPI 后台任务:BackgroundTasks 的使用场景与注意事项 FastAPI配置管理避坑指南:从硬编码到 .env 与 pydantic_settings 类,连路由用法都给你捋清楚 FastAPI 文件上传避坑全指南:分块存盘、类型校验与安全兜底 FastAPI + Pydantic 模型终极实战手册:从能跑就行到固若金汤,这些技巧你一定用得上 FastAPI + SQLAlchemy 2.0 通用CRUD操作手册 —— 从同步到异步,一次讲透 FastAPI订单防超卖实战:从数据库锁到Saga分布式事务,这一篇给你理清了 FastAPI 生产环境避坑指南:用 Alembic 管理数据库迁移,别再手动改表结构了! FastAPI服务半夜又挂了?先别急着重启,查查你的数据库连接池“池子”是不是漏了 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 Vue 3 组合式 API 香是香,但从Vue2迁移时你可别像我当初一样踩进这 3 个深坑里 我用fastapi-scaff搭了个项目,两天工期缩到两小时,老板以为我开挂了 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! FastAPI自动生成的API文档太丑?我花了一晚上把它改成了客户愿意付费的样子 告别手写 API 胶水代码:FastAPI 与 Vue 的“契约自动机” OpenAPI 实战 FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南” FastAPI + Celery 实战:异步任务里调用 Redis 和数据库的全解析,及生产级组织方案 FastAPI里玩转Redis和数据库的正确姿势,别让异步任务把你坑哭了! FastAPI + Celery 实战:异步任务的坑与解法,我帮你踩了一遍 FastAPI子应用挂载:别再让root_path坑你一夜 FastAPI项目半夜报警吵醒你?聊聊告警这事儿怎么搞! 别再数据线了!用FastAPI 5分钟搭个局域网文件+剪贴板神器 FastAPI状态共享秘籍:别再让中间件、依赖和路由“各自为政”了! FastAPI实战:WebSocket vs Socket.IO,这回真给我整明白了! 从0到1,FastAPI + PostgreSQL + Tortoise ORM 实战避坑指南 FastAPI + PostgreSQL 实战:给应用装上“缓存”和“日志”翅膀 FastAPI + PostgreSQL 实战:从入门到不踩坑,一次讲透
FastAPI单元测试实战:别等上线被喷才后悔,TestClient用对了真香!
一名程序媛呀 · 2026-03-25 · via 博客园 - 一名程序媛呀

🎯 摘要:你是不是也遇到过——改了一行代码,结果线上某个接口悄悄崩了?测试全靠 postman 手点?今天手把手带你用FastAPI的 TestClient 写出靠谱单元测试,把bug扼杀在摇篮里。

咱们先问个扎心的问题:你上次写完一个新接口,是不是直接postman跑通就美滋滋地推到生产了?😎 然后半夜被报警电话吵醒,发现一个你没测到的边界条件炸了,用户数据乱飘,老板发火,你还要一边道歉一边修bug……

单元测试不是给领导看的,是给自己的代码上保险。 今天咱们不聊虚的,直接撸袖子,把FastAPI的TestClient从安装到排雷全讲明白。

📌 本文能帮你解决什么

✅ 快速搭建FastAPI项目的单元测试环境,不再手动模拟请求

✅ 掌握 TestClient 的常用方法(GET/POST/文件上传/依赖项覆盖)

✅ 避开我踩过的3个大坑:数据库连接异步测试依赖注入

✅ 写出可维护、能快速定位问题的测试代码,自信重构

🔧 第一部分:TestClient 到底是什么?

你可能会问:“单元测试难道不是用 requests 库去怼我本地启动的服务吗?” 那样太慢且耦合重!

FastAPI自带的 TestClient 基于 Starlette 的测试框架,直接复用你app的实例,不需要真正启动服务器。 就像你吃火锅不用先把锅烧开再涮肉,而是直接在后厨试吃——速度快、隔离好,而且能精准mock依赖。

💡 我的个人心得:把TestClient当成你写的每个接口的“贴身保镖”。你每加一个路由,就应该立刻写一个保镖测试用例,确认它能正常干活。

🚀 第二部分:3分钟搭好测试架子(含安装)

1. 安装测试必备库

pip install pytest httpx pytest-asyncio
# TestClient 是 fastapi.testclient 自带的,但依赖 httpx

很多教程只让你装 pytest,但忽略了一点:FastAPI 的 TestClient 底层需要 httpx,如果你用异步端点,一定记得装 pytest-asyncio。别学我当初只装 pytest 然后疯狂报错“loop already running”,气得差点砸电脑。

2. 最小示例:测试一个“Hello World”

# app/main.py
from fastapi import FastAPI
app = FastAPI()

@app.get("/")
def read_root():
    return {"msg": "Hello FastAPI"}
# tests/test_main.py
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_read_root():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"msg": "Hello FastAPI"}
    
# 在项目根目录执行 uv run -m pytest

看到没?直接 client.get("/"),就像前端发请求一样简单,但速度快到飞起⚡️。

🧪 第三部分:实战演练——那些常用到爆的测试方法

🎯 场景1:POST 请求 + JSON 数据

假设你有一个创建用户的接口,需要传body。写测试时,直接模拟真实请求体:

def test_create_user():
    payload = {"name": "小媛", "email": "yuan@coder.com"}
    response = client.post("/users/", json=payload)
    assert response.status_code == 201
    data = response.json()
    assert data["name"] == "小媛"
    assert "id" in data

关键点:一定要用 json= 参数传字典,而不是 data=,后者默认是表单编码,后端用pydantic模型接会报422!别问我怎么知道的……那次我排查了半小时才发现传参方式错了。

🎯 场景2:文件上传测试(血的教训)

上传头像这类接口,用TestClient时记得用files参数:

def test_upload_avatar():
    with open("tests/fake_avatar.jpg", "rb") as f:
        response = client.post(
            "/users/avatar/",
            files={"file": ("avatar.jpg", f, "image/jpeg")}
        )
    assert response.status_code == 200

这里容易翻车的是:文件对象必须在with块内,并且要传三元组 (filename, fileobj, content_type) 才能模拟完整的上传行为。我早期偷懒只传了文件句柄,结果服务端拿不到文件名,直接报500。

⚠️ 第四部分:避坑专区——3个高频痛点解决方案

1️⃣ 数据库依赖怎么隔离?

千万别让测试跑到你开发库里去!使用 依赖覆盖(override) + 独立测试数据库

FastAPI 的 app.dependency_overrides 是神器,可以把生产用的数据库session替换成测试专用的。

# 在 conftest.py 或 test 文件里
from app.dependencies import get_db
from app.db import TestingSessionLocal

def override_get_db():
    db = TestingSessionLocal()
    try:
        yield db
    finally:
        db.close()

app.dependency_overrides[get_db] = override_get_db

2️⃣ 异步端点怎么测?

如果你用 async def 定义路由,TestClient默认是同步的,但依旧可以直接调用!只是当你需要异步数据库操作时,要用 pytest.mark.asyncio 去测试内部的service。但TestClient本身仍是同步调用,因为它内部已经处理了异步。

最保险方案:在测试配置中加上 pytest_plugins = ('pytest_asyncio',),并对异步辅助函数做特殊标记。

3️⃣ 测试之间互相污染数据?

每一个测试函数运行前,务必清理数据库!我习惯在每个测试前用 @pytest.fixture(autouse=True) 清空关键表,或者直接使用事务回滚策略,保证测试彼此独立。

🎁 第五部分:让测试飞起来——进阶小技巧

当你项目越来越大,每个接口都手动写断言会想吐。可以封装一个通用断言函数,统一处理状态码和返回结构。

另外,不要只测 happy path,一定要测错误路径:401、404、422 这些状态码往往隐藏最多bug。

🚀 我的私藏习惯:每次写新功能,先写一个“一定会失败”的测试(比如断言返回404),然后看着它变红,再写代码让它变绿。这叫 TDD 的红-绿-重构,心里踏实得不行。

📝 最后啰嗦两句

咱们程序员,经常被前端催、产品催,很容易觉得写测试浪费时间。但说句掏心窝子的话:你花半小时写测试,能省下三个通宵修bug的时间。

TestClient本身用起来极其顺手,几乎没有心智负担。我把这些坑都替你踩过了,剩下的就是你现在打开编辑器,给最近写的接口补个测试,哪怕只补一个呢,也是对自己的代码负责。

如果你在实践过程中遇到什么奇葩报错,或者想看我下次聊“异步数据库测试实战”,欢迎在评论区告诉我~ 我会不定期把大家的问题整理成新的文章。