测试别踩坑!FastAPI隔离数据库+Mock用户,守住职场安全线

9 阅读4分钟

fI7IrVQfs

深夜,程序员小王对着一条条的错误信息发愁。

他的 FastAPI 应用程序明明在服务器上运行良好,但一旦运行测试,就连最简单的健康检查都报错。

这不是他一个人的战斗,而是许多开发者面临的共同困境。

问题根源在于:测试环境与生产环境的纠缠

测试与生产的 “隔离” 难题

很多开发者对测试抱有误解:

  • 以为用了 pytest 就是真测试
  • 以为跑通了就是没问题了
  • 以为 “测试数据库” 只是个概念

直到某天,测试删光了用户表,或是性能测试拖垮了线上服务。

才恍然大悟:测试环境和生产环境根本没有隔离。

FastAPI 应用非常容易踩坑,在测试时,若不加注意,就会直接调用生产环境。

FastAPI 测试的正确打开方式:隔离、模拟、覆盖

真正的测试的三个核心要素:

  1. 隔离的测试数据库 — 绝不碰生产数据
  2. 模拟的依赖项 — 控制测试环境的所有输入
  3. 覆盖的依赖注入 — 让 FastAPI 在测试时,换 “演员
00、准备

安装依赖:

pip install httpx -i https://pypi.tuna.tsinghua.edu.cn/simple

重点注意:

本期,需要使用 IDE 打开 Chapter_14 目录(TodoApp 目录上一级)

项目根目录变了,项目中所有的导入路径,都需要调整。这里很麻烦,需要一个一个的去改。

此处省略一万字 ......。

大家记得找我要配套的项目代码。直接私信我,发送 FastAPI

修改完成后,在终端启动应用程序

uvicorn TodoApp.main:app --reload

程序运行正常,就算完成了。

01、创建专属测试数据库

进入 TodoApp/test 目录,创建 test_todos.py 文件(针对 todos.py 文件进行测试)

在文件中添加测试数据库地址、测试引擎、测试会话等,这些属于测试环境,与生产环境相隔离。

from sqlalchemy import create_engine
from sqlalchemy.pool import StaticPool
from sqlalchemy.orm import sessionmaker
from ..database import Base

# 测试数据库地址
SQLALCHEMY_DATABASE_URL = "sqlite:///./testdb.db"


# 创建测试引擎,StaticPool确保连接复用
engine = create_engine(
    SQLALCHEMY_DATABASE_URL,
    connect_args={"check_same_thread": False},
    poolclass=StaticPool
)


# 创建测试会话
TestSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


Base.metadata.create_all(bind=engine)
02、覆盖数据库依赖

用测试会话替换生产的数据库会话,这样测试时就会连接测试数据库

# 覆盖数据库依赖
def override_get_db():
    db = TestingSessionLocal()
    try:
        yield db
    finally:
        db.close()

# 告诉 FastAPI:测试时用这些替代品
app.dependency_overrides[get_db] = override_get_db
03、Mock 已登录用户

很多接口需要用户登录才能访问,直接测试会报错,我们可以模拟用户信息绕过登录验证。


# 覆盖当前用户依赖(无需真实登录!)
def override_get_current_user():
    return {
        "username": "wangerge",
        "id": 1,
        "user_role": "admin"
    }

# 告诉 FastAPI:测试时用这些替代品
app.dependency_overrides[get_current_user] = override_get_current_user

做完这三步,就可以测试需要数据库和登录的接口了。

04、编写接口测试

添加待办事项列表接口测试

client = TestClient(app)

def test_read_all_authenticated():
    response = client.get("/todos")
    assert response.status_code == status.HTTP_200_OK
    assert response.json() == []

我们在终端,切换到 Chapter_14 目录,执行接口测试:

(.venv) wangerge_notes: Chapter_14$ pytest --disable-warnings
==================== test session starts ====================
collected 3 items                                                                                            

TodoApp/test/test_example.py .                                                                         [ 33%]
TodoApp/test/test_main.py .                                                                            [ 66%]
TodoApp/test/test_todos.py .                                                                           [100%]

==================== 3 passed, 2 warnings in 2.82s ====================
(.venv) wangerge_notes: Chapter_14$ 

这样,测试一切正常。

写在最后

测试不是 CheckList,不是 “写完就行” 的任务。它是代码的镜子,是质量的守护者。

从今天开始,重建你对测试的信任 —— 从配置一个隔离的测试环境开始。

接下来,我们将一起讨论:创建数据接口的测试要怎么写?删除数据接口要怎么写?如果数据没找到时,接口测试要怎么写?

下期,我将掰开了,揉碎了,把它们一次性讲清楚。

想要获取本章完整代码,请在评论区回复 【FastAPI】,代码直接复制就能跑。

关于 FastAPI 的其他疑问

新功能上线就崩?Pytest三步测试法,让你的FastAPI稳如老狗,Bug率直降80%

加个字段,服务崩了?FastAPI新手避坑,Alembic三步搞定表结构变更!方案闭眼抄

别等着被骂:API上线前,一定要把SQLite换成MySQL,附 FastAPI对接代码

别等被骂才后悔:APP上线前,一定要把SQLite换成PostgreSQL,附 FastAPI对接代码

你的API在裸奔?踩坑8小时,从“越权裸奔”到“权限严控”:FastAPI+JWT+依赖注入,这套方案闭眼抄

相关内容我都给大家做好了,感兴趣的朋友来「我的主页」找一找,直接就可以看到。

欢迎关注 「王二哥的技术笔记」,每天分享「Python」、「职场」有趣干货,千万不要错过!