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

Flask在线图书馆项目路由测试遇SQLAlchemy RuntimeError求助

解决Flask测试中SQLAlchemy实例未注册的RuntimeError

问题描述

开发在线图书馆项目时,编写路由测试用例,test_get_registration_form和test_get_home测试成功,但test_get_authors抛出如下错误:

RuntimeError:The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances?

相关代码

conftest.py

import pytest
from flask import Flask
from routes import blueprint, login_manager
from flask_sqlalchemy import SQLAlchemy

@pytest.fixture(scope='function')
def app():
    app = Flask(__name__, template_folder="../templates")
    app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///" + "../instance/test_db.db"
    app.config['TESTING'] = True
    app.config['SECRET_KEY'] = 'secret_key'
    login_manager.init_app(app)
    app.register_blueprint(blueprint)
    return app

@pytest.fixture(scope='function')
def db(app):
    db = SQLAlchemy(app=app)
    return db

@pytest.fixture(scope="function")
def client(app):
    return app.test_client()

test_routes.py

def test_get_registration_form(client):
    response = client.get('/registration')
    assert response.status_code == 200
    assert b'<!DOCTYPE html>' in response.data
        

def test_get_home(client):
    response = client.get('/')
    assert response.status_code == 200
    assert b'<!DOCTYPE html>' in response.data
    assert b'class="login-form' in response.data


def test_get_authors(client):
    response = client.get('/authors')
    assert b'<!DOCTYPE html>' in response.data

routes.py

@blueprint.route('/authors', methods=["GET", "POST"])
def get_authors():
    form = AuthorForm()
    authors = Author.query.all()
    return render_template("authors.html", authors=authors, form=form)

问题原因

你在测试fixture中新建了一个SQLAlchemy实例,但路由中Author.query依赖的是项目里另一个全局SQLAlchemy实例,两个实例没有关联,导致测试时Author模型找不到绑定的Flask app。

修复步骤

1. 统一项目的SQLAlchemy实例

在项目中创建一个全局的SQLAlchemy实例(通常放在models.py中),所有模型都基于这个实例创建:

# models.py
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Author(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)

然后在routes.py中导入这个全局实例和模型:

from models import db, Author

2. 修改测试fixture,复用全局SQLAlchemy实例

更新conftest.py,不要新建SQLAlchemy实例,而是导入全局的db,并绑定到测试app,同时创建测试用的数据库表:

# conftest.py
import pytest
from flask import Flask
from routes import blueprint, login_manager
from models import db  # 导入全局db实例

@pytest.fixture(scope='function')
def app():
    app = Flask(__name__, template_folder="../templates")
    # 使用内存SQLite数据库,测试后自动销毁,避免污染本地文件
    app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///:memory:"
    app.config['TESTING'] = True
    app.config['SECRET_KEY'] = 'secret_key'
    
    login_manager.init_app(app)
    db.init_app(app)  # 将全局db绑定到测试app
    app.register_blueprint(blueprint)
    
    # 在app上下文内创建所有表
    with app.app_context():
        db.create_all()
    return app

@pytest.fixture(scope='function')
def db_session(app):
    # 提供带事务回滚的数据库会话,避免测试数据互相干扰
    with app.app_context():
        connection = db.engine.connect()
        transaction = connection.begin()
        session = db.create_scoped_session(options={"bind": connection})
        db.session = session
        
        yield session
        
        # 测试结束后回滚事务,清理数据
        transaction.rollback()
        connection.close()
        session.remove()

@pytest.fixture(scope="function")
def client(app):
    return app.test_client()

3. (可选)优化测试用例,添加测试数据

如果需要验证页面是否正确显示数据,可以借助db_session fixture添加测试数据:

# test_routes.py
def test_get_authors(client, db_session):
    # 添加测试作者
    test_author = Author(name="测试作者")
    db_session.add(test_author)
    db_session.commit()
    
    response = client.get('/authors')
    assert response.status_code == 200
    assert b'<!DOCTYPE html>' in response.data
    assert b'测试作者' in response.data

内容的提问来源于stack exchange,提问作者Al'kator

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 00:27:53