Skip to content
标题:Pytest + TestClient 重构
描述:介绍了从SQLite迁移到PostgreSQL后,使用Pytest+TestClient重构后端项目测试用例的方案。核心是通过fixture实现测试数据库隔离,确保测试连接正确的数据库。
发布日期:2026/01/01
标签:
pytesttestclient

背景描述

  • 一个后端项目,主要是对数据库中8张表的增删改查操作。之前使用的是 sqlite ,现在数据已经迁移到远程的 postgresql 。后端做了一些修改,启动后,部分功能正常,部分功能有问题。
  • 项目之前是有一些测试用例的,使用了 Pytest + TestClient 技术栈。不过它们似乎部分还是顽固地连接到了之前的 sqlite 中去了,这让我怀疑这些测试用例整体上的可靠性。

重构思路

不需要彻底重写所有测试用例,但需要系统性地重构和修复,以确保测试的可靠性、可维护性和环境隔离性。

1. 首要任务:隔离测试数据库,杜绝“连错库”

  • 使用测试专用的 PostgreSQL 数据库(如 yourapp_test),而非开发或生产库。
  • pytest 中通过 fixture + 环境变量 动态注入数据库 URL
python
   # conftest.py
   import os
   import pytest
   from sqlalchemy import create_engine
   from sqlalchemy.orm import sessionmaker
   from your_app.db import Base

   TEST_DB_URL = os.getenv("TEST_DATABASE_URL", "postgresql://user:pass@localhost/yourapp_test")

   @pytest.fixture(scope="session")
   def test_engine():
       engine = create_engine(TEST_DB_URL)
       Base.metadata.create_all(bind=engine)  # 创建测试表结构
       yield engine
       Base.metadata.drop_all(bind=engine)  # 清理

   @pytest.fixture
   def db_session(test_engine):
       Session = sessionmaker(bind=test_engine)
       session = Session()
       try:
           yield session
       finally:
           session.rollback()
           session.close()
  • 确保所有测试文件通过 fixture 注入 session,而非直接 import 全局 session。否则容易污染主应用的数据库连接配置。

2. 重构策略:保留逻辑,替换依赖

原测试用例结构是合理的(分层:API → CRUD → Model),逻辑可复用,只需修复数据库连接和事务管理

  • 保留:测试场景、断言逻辑、边界条件(如分页、日期筛选)。
  • 替换/修复
    • 所有数据库连接初始化方式;
    • 事务回滚机制(确保每个测试用例后自动清理数据);
    • 模型创建方式(确保使用测试 session 而非全局 session)。

3. 关键改进点

1. 统一数据库连接配置

  • 主应用和测试应通过 同一套配置机制(如 Pydantic Settings 或环境变量)读取 DATABASE_URL
  • 测试时通过 pytest --override-ini=...env TEST_DATABASE_URL=... pytest 指定测试库。

2. 使用事务回滚实现测试隔离

对每个测试用例,在 setup 时开启事务,teardown 时 rollback(而非 commit),避免数据残留。示例:

python
@pytest.fixture
def clean_db(db_session):
    transaction = db_session.begin()
    yield db_session
    transaction.rollback()

3. 删除冗余测试文件

原有 9 个测试文件,存在明显重叠:

  • test_db_connection.pytest_db_simple.pytest_db_operations.py 功能高度重复 → 合并为一个 test_database.py
  • test_login_logout.pytest_api_with_auth.py 也有重叠 → 保留后者,前者可删
  • test_weight_records.py 功能已被 test_crud.pytest_api.py 覆盖 → 可删除或保留作为专项压力测试

🧹 精简后应保留 4 类测试:

  1. test_models.py(模型结构)
  2. test_crud.py(数据库操作)
  3. test_api.py(含认证的端到端 API 测试)
  4. test_database.py(连接与基础读写)

4. 增加 PostgreSQL 特性兼容性测试

SQLite 与 PostgreSQL 在以下方面有差异,需专项验证:

  • 大小写敏感(PostgreSQL 表名/列名默认小写);
  • 数据类型(如 DATETIME vs TIMESTAMP);
  • 自增 ID 行为;
  • 字符串比较(ILIKE vs LIKE);
  • 分页语法(虽然用 ORM 可能屏蔽了,但需确认)。

4. 验证迁移是否成功:关键检查清单

项目检查方式
所有测试连接的是 PostgreSQL在测试中打印 engine.url,确认无 sqlite
每个测试用例独立、无状态运行单个测试、多个测试、乱序运行,结果一致
数据在测试后自动清理测试结束后查表应为空或初始状态
分页/筛选/排序结果正确对比 SQLite 与 PostgreSQL 返回结果是否一致
错误处理正常(如唯一约束、非空约束)PostgreSQL 约束更严格,需验证异常被捕获

5. 行动建议(分步)

  1. 立即:建立独立的 yourapp_test PostgreSQL 数据库。
  2. 修改:配置系统,支持通过环境变量切换 DB URL。
  3. 重构 conftest.py,引入 fixture 管理测试 DB 生命周期。
  4. 逐个修复测试文件,替换硬编码连接,使用 fixture 注入 session。
  5. 删除冗余测试文件,合并重复逻辑。
  6. 运行全量测试,确认:
    • 所有测试通过;
    • 无连接 SQLite 的日志;
    • 测试数据库在运行后为空。

准备环境 - 数据库

pytest 要求连接到一个没有数据的空数据库中

用 PuTTY 连接到远程服务器

  • 用 deploy 这个用户登录到远程服务器
bash
# 创建工作目录
mkdir -p ~/pg_migration
# 进入工作目录
cd ~/pg_migration
# 执行 pg_dump 命令,将开发数据库的 schema 导出到 create_schema.sql 文件中
pg_dump -h localhost -U kpfit_dev -d kpfit_dev_db --schema-only --no-owner --no-privileges -f create_schema.sql

通过 FileZilla 下载 create_schema.sql 文件

  • 用 deploy 这个用户,通过 FileZilla 连接到远程服务器。注意:用户名和 PuTTY 中配置的一致。
  • 从远程服务器上的 ~/pg_migration 目录下载 create_schema.sql 文件到本地。

DBeaver 中执行 create_schema.sql 文件

  • 注意:DBeaver 作为一个标准的 SQL 客户端,不认识 \ 开头的命令,所以会报语法错误。所以应先查找并删除所有以 \ 开头的行:

    • 删除第一行的 \restrict ...。
    • 删除最后一行的 \unrestrict ...。
  • 用 DBeaver 连接到测试数据库(kpfit_test_db)。

  • 打开 create_schema.sql 文件,点击右键选择 Run as SQL Script

  • 确认执行后,测试数据库应包含与开发数据库相同的 schema。

测试用例重构

文件目录结构

plaintext
    1 keepfit\
    2 └── server\
    3     └── tests\                 # pytest 测试用例主目录
    4         ├── __init__.py       # Python 包标识
    5         ├── conftest.py       # pytest 配置和 fixture 定义
    6         ├── test_api.py       # API 接口测试
    7         ├── test_crud.py      # CRUD 操作测试
    8         ├── test_database.py  # 数据库相关测试
    9         └── test_models.py    # 数据模型测试

测试用例开发顺序

开发顺序(自底向上)

  • conftest.py (工装夹具)
  • test_database.py (验证地基)
  • test_models.py (验证砖块)
  • test_crud.py (验证砌墙)
  • test_api.py (验证整栋楼)

测试用例的执行

bash
# 验证数据库连接
pytest -v tests/test_database.py
# 验证模型
pytest -v tests/test_models.py
# 验证 CRUD 操作
pytest -v tests/test_crud.py
# 验证 API 接口
pytest -v tests/test_api.py
# 执行所有测试
pytest -v --tb=short

基于 MIT 许可发布。