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

修改Pytest Fixture中SQLALCHEMY_DATABASE_URI仍用生产库的问题

问题

我正在学习flask-sqlalchemy库,使用Flask框架开发应用,并通过Pytest结合fixtures进行测试。尝试在测试fixtures中覆盖应用配置里的sqlalchemy_database_uri,但测试仍在使用“生产”数据库而非临时数据库——通过fixtures创建的测试记录都出现在应用初始化时创建的数据库中。

按以下目录结构,在根目录执行pytest后,测试可正常收集、运行并通过,但测试结束后instance文件夹中生成了新的hello.db数据库。希望测试使用运行后即消失的内存型SQLite数据库,请问该如何编写/配置测试以实现这一需求?

# 目录结构
.
├── mini_app
│   ├── __init__.py
│   ├── app.py
│   ├── config.py
│   └── models.py
└── tests
    ├── __init__.py
    └── test_all.py

现有代码

app.py

from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from mini_app import config

db = SQLAlchemy()

def create_app(cfg= None):
    if cfg is None:
        cfg = config.Config()
    app = Flask(__name__)
    app.config.from_object(cfg)

    # Init_apps
    db.init_app(app)

    @app.route("/square")
    def square():
        """A view which uses no templates and no database."""
        number = int(request.args.get("number", 0))
        return str(number ** 2)

    return app

config.py

class Config:
    SQLALCHEMY_DATABASE_URI = "sqlite:///hello.db"

models.py

from mini_app.app import db

class Author(db.Model):
    __tablename__ = "authors"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String)

test_all.py

import pytest

import  mini_app.app
import  mini_app.models

@pytest.fixture
def client():
    # Prepare before your test
    app = mini_app.app.create_app()
    app.config["TESTING"] = True
    app.testing = True
    app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite://"

    with app.test_client() as client:
        with app.app_context():
            mini_app.app.db.create_all()
            author1 = mini_app.models.Author(id=1, name="foo")
            mini_app.app.db.session.add(author1)
            mini_app.app.db.session.commit()
        # Give control to the test
        yield client

        # Cleanup after the test run.
        # ... nothing here, for this simple example


def test_square(client):
    rv = client.get("/square?number=8")
    assert b"64" == rv.data

解决方案

问题根源

当前写法是先调用create_app()初始化应用(此时已加载默认Config的数据库URI),之后再修改app.config。但db.init_app(app)已经在初始化应用时执行,SQLAlchemy已经绑定了原数据库URI,后续修改配置不会生效,导致测试依然使用文件型SQLite数据库。

修正方案

方式一:直接在创建应用时传入测试配置

修改test_all.py的fixture,确保在create_app时就传入测试专用配置,让SQLAlchemy初始化时绑定内存数据库:

import pytest
from mini_app.app import create_app, db
from mini_app.models import Author

@pytest.fixture
def app():
    # 定义测试专用配置
    test_config = {
        "TESTING": True,
        "SQLALCHEMY_DATABASE_URI": "sqlite://",
        "SQLALCHEMY_TRACK_MODIFICATIONS": False  # 关闭修改追踪,避免不必要的警告
    }
    # 创建应用并加载测试配置
    app = create_app()
    app.config.from_mapping(test_config)

    with app.app_context():
        # 创建数据表并添加测试数据
        db.create_all()
        author1 = Author(id=1, name="foo")
        db.session.add(author1)
        db.session.commit()
        yield app

        # 测试后清理:移除会话并删除所有表
        db.session.remove()
        db.drop_all()

@pytest.fixture
def client(app):
    # 基于测试应用生成客户端
    return app.test_client()

def test_square(client):
    rv = client.get("/square?number=8")
    assert b"64" == rv.data

方式二:添加测试专用配置类(更适合大型项目)

在config.py中新增测试配置类,让测试直接调用该类:

# config.py
class Config:
    SQLALCHEMY_DATABASE_URI = "sqlite:///hello.db"
    SQLALCHEMY_TRACK_MODIFICATIONS = False

class TestConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = "sqlite://"

然后修改test_all.py的fixture:

import pytest
from mini_app.app import create_app, db
from mini_app.config import TestConfig
from mini_app.models import Author

@pytest.fixture
def app():
    # 直接传入测试配置类创建应用
    app = create_app(cfg=TestConfig)

    with app.app_context():
        db.create_all()
        author1 = Author(id=1, name="foo")
        db.session.add(author1)
        db.session.commit()
        yield app

        db.session.remove()
        db.drop_all()

@pytest.fixture
def client(app):
    return app.test_client()

def test_square(client):
    rv = client.get("/square?number=8")
    assert b"64" == rv.data

关键说明

  • 必须确保SQLAlchemy初始化(db.init_app(app))时,应用已经加载了测试配置的数据库URI,否则后续修改配置不会生效。
  • 拆分app和client fixture,既可以让测试复用应用实例,也能更清晰地管理应用的生命周期和清理逻辑。
  • 测试后添加db.session.remove()和db.drop_all(),确保内存数据库的资源被正确释放,避免测试间的状态污染。

内容的提问来源于stack exchange,提问作者Alvaro Aguilar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 15:55:19