You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask应用中PyTest未切换测试数据库致重复条目错误

解决Flask+PyTest测试时无法切换到测试数据库的问题

问题背景

本地.env文件配置了两个数据库:

  • DATABASE_URI(生产/开发库)
  • TEST_DB_URI(测试专用库)

运行PyTest测试套件时,频繁出现重复条目错误:

sqlalchemy.exc.IntegrityError: (pymysql.err.IntegrityError) (1062, "Duplicate entry 'test@example.com' for key 'users.email'")

原因是测试代码实际连接的是DATABASE_URI对应的数据库(该库已存在测试用户),而非预期的TEST_DB_URI测试库,即使在pytest fixture中手动修改了app.config["SQLALCHEMY_DATABASE_URI"]也未生效。

核心原因

你的app.py中,在初始化阶段就直接将app传入SQLAlchemy()创建了db实例:

db = SQLAlchemy(app)

这会导致db实例在程序启动时就绑定了当时的SQLALCHEMY_DATABASE_URI配置(即DATABASE_URI)。后续在pytest fixture中修改app.config,并不会更新已创建的db实例的连接配置,测试代码依然会使用初始绑定的生产库。

另外,原fixture的执行顺序存在错误:在yield client之前就执行了db.drop_all(),导致测试运行时数据库表已被删除,这也是潜在问题。

解决方案

1. 修改Flask扩展初始化方式

将SQLAlchemy、Migrate等扩展的初始化延迟,先创建空实例,再通过init_app()绑定到app,这样测试时可以重新绑定配置。

修改app.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate

# 创建Flask app
app = Flask(__name__, static_url_path="/static")
# 加载基础配置
app.config.from_pyfile("config.py")

# 先创建空的扩展实例,不直接传入app
db = SQLAlchemy()
migrate = Migrate()
login_manager = LoginManager()

# 初始化扩展,绑定到app
db.init_app(app)
migrate.init_app(app, db)
login_manager.init_app(app)

2. 修正Pytest Fixture逻辑

在fixture中修改app配置后,重新初始化db确保使用测试数据库,同时调整执行顺序,确保测试完成后再清理数据库。

修改tests/__init__.py:

import os
import pytest
from app import app, db
from models.user import User

@pytest.fixture
def client():
    # 覆盖配置,启用测试模式并指定测试数据库
    app.config["TESTING"] = True
    app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv("TEST_DB_URI")
    
    # 重新初始化db,确保使用新的配置
    db.init_app(app)

    with app.test_client() as client:
        with app.app_context():
            # 创建测试所需的所有表
            db.create_all()
            # 添加测试用户
            user = User(email="test@example.com", password="test_password")
            db.session.add(user)
            db.session.commit()
        # 将client返回给测试用例执行
        yield client
        # 测试完成后,清理数据库
        with app.app_context():
            db.session.remove()
            db.drop_all()

3. 优化基础配置(可选)

修改config.py,将TESTING默认设置为False,避免开发环境默认使用测试配置:

import os
from dotenv import load_dotenv
load_dotenv()

SECRET_KEY = os.environ['SESSION_KEY']
SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URI']
SQLALCHEMY_TRACK_MODIFICATIONS = True

# General Config
ENVIRONMENT = "development"
FLASK_APP = "app"
FLASK_DEBUG = True
TESTING = False  # 改为False,测试时由fixture覆盖

验证方法

运行测试套件后,检查TEST_DB_URI对应的数据库,会发现测试用户被正确创建并在测试结束后自动删除,同时生产库不会再被测试代码修改。

内容的提问来源于stack exchange,提问作者Quinn Moffat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 03:17:19