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

Flask+pytest运行测试时出现SQLALCHEMY_DATABASE_URI未设置错误

Flask+SQLAlchemy集成测试报错:RuntimeError: Either 'SQLALCHEMY_DATABASE_URI' or 'SQLALCHEMY_BINDS' must be set

问题场景

Flask应用使用SQLAlchemy管理数据库,Postman手动测试一切正常,但运行pytest tests/时触发上述错误,已在.flaskenv中配置SQLALCHEMY_DATABASE_URI变量。

相关代码与报错信息

测试代码片段

import pytest
import json
from backend.app import app


@pytest.fixture(autouse=True)
def load_env_vars():
    import dotenv

    dotenv.load_dotenv(dotenv_path=".flaskenv")


@pytest.fixture
def client():
    app.config["TESTING"] = True
    with app.test_client() as client:
        yield client


# Test login endpoint
def test_login_user(client):
    payload = {"username": "testuser", "password": "password123"}
    response = client.post("/login", json=payload)
    assert response.status_code == 200
    assert "token" in response.json

报错栈信息

========================== test session starts ===========================
platform win32 -- Python 3.7.4, pytest-7.4.2, pluggy-1.2.0
rootdir: D:\link_b_flaskapp\backend
plugins: dotenv-0.5.2
collected 0 items / 1 error

================================= ERRORS =================================
_______________ ERROR collecting tests/test_integration.py _______________ 
.venv\lib\site-packages\_pytest\runner.py:341: in from_call
    result: Optional[TResult] = func()
.venv\lib\site-packages\_pytest\runner.py:372: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect").venv\lib\site-packages\_pytest\python.py:531: in collect
    self._inject_setup_module_fixture()
.venv\lib\site-packages\_pytest\python.py:545: in _inject_setup_module_fixture
    self.obj, ("setUpModule", "setup_module")
.venv\lib\site-packages\_pytest\python.py:310: in obj
    self._obj = obj = self._getobj()
.venv\lib\site-packages\_pytest\python.py:528: in _getobj
    return self._importtestmodule()
.venv\lib\site-packages\_pytest\python.py:617: in _importtestmodule        
    mod = import_path(self.path, mode=importmode, root=self.config.rootpath)
.venv\lib\site-packages\_pytest\pathlib.py:567: in import_path
    importlib.import_module(module_name)
C:\Python\Python37\lib\importlib\__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
<frozen importlib._bootstrap>:1006: in _gcd_import
    ???
<frozen importlib._bootstrap>:983: in _find_and_load
    ???
<frozen importlib._bootstrap>:967: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:677: in _load_unlocked
    ???
.venv\lib\site-packages\_pytest\assertion\rewrite.py:178: in exec_module   
    exec(co, module.__dict__)
tests\test_integration.py:4: in <module>
    from backend.app import app
app.py:5: in <module>
    app = create_app()
__init__.py:93: in create_app
    db.init_app(app)
.venv\lib\site-packages\flask_sqlalchemy\extension.py:311: in init_app     
    "Either 'SQLALCHEMY_DATABASE_URI' or 'SQLALCHEMY_BINDS' must be set."  
E   RuntimeError: Either 'SQLALCHEMY_DATABASE_URI' or 'SQLALCHEMY_BINDS' must be set.
======================== short test summary info ========================= 
ERROR tests/test_integration.py - RuntimeError: Either 'SQLALCHEMY_DATABASE_URI' or 'SQLALCHEMY_BINDS' m...
!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!! 
============================ 1 error in 1.81s ============================

应用__init__.py文件内容

from flask import Flask, jsonify
from datetime import timedelta

from flask_sqlalchemy import SQLAlchemy


# local imports
from .config import app_config
from .extensions import jwt, db, bcrypt, migrate
from .models import TokenBlocklist


def create_app():
    app = Flask(__name__)
    app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("SQLALCHEMY_DATABASE_URI")
    app.config["JWT_SECRET_KEY"] = os.environ.get("JWT_SECRET_KEY")
    app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = os.environ.get("DATABASE_TRACK_MODIFICATIONS")
    app.config["JWT_ALGORITHM"] = os.environ.get("JWT_ALGORITHM")
    app.config["FLASK_DEBUG"] = os.environ.get("FLASK_DEBUG")


    jwt.init_app(app)
    db.init_app(app)
    bcrypt.init_app(app)
    migrate.init_app(app, db)

    # Register blueprints and initialize extensions here
    from .routes.user_routes import user_bp
    from .routes.admin_routes import admin_bp
    from .routes.auth_routes import auth_bp

    app.register_blueprint(user_bp, url_prefix="/user")
    app.register_blueprint(auth_bp, url_prefix="/auth")
    app.register_blueprint(admin_bp, url_prefix="/admin")

    return app

.flaskenv文件内容

FLASK_DEBUG = 1
SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://root:root@localhost/app"
SQLALCHEMY_TRACK_MODIFICATIONS = False
# jw configuration
JWT_SECRET_KEY = "secret_key"
JWT_ALGORITHM = "HS256"

问题原因

  1. 环境变量加载时机滞后:测试文件中先导入backend.app,再通过fixture加载环境变量。而app = create_app()在导入时就会执行,此时os.environ还未读取.flaskenv中的变量,导致SQLALCHEMY_DATABASE_URI被设置为None,触发SQLAlchemy的初始化检查错误。
  2. __init__.py缺失os模块导入:代码中使用了os.environ.get()但未导入os模块,属于潜在报错点。

解决方案

方案1:调整测试文件的环境变量加载顺序

将环境变量加载放在导入app之前,确保create_app()执行时环境变量已生效:

import pytest
import json
import dotenv

# 先加载环境变量,再导入app
dotenv.load_dotenv(dotenv_path=".flaskenv")

from backend.app import app


@pytest.fixture(autouse=True)
def load_env_vars():
    # 无需重复加载,或可留空
    pass


@pytest.fixture
def client():
    app.config["TESTING"] = True
    with app.test_client() as client:
        yield client


def test_login_user(client):
    payload = {"username": "testuser", "password": "password123"}
    response = client.post("/login", json=payload)
    assert response.status_code == 200
    assert "token" in response.json

方案2:修复__init__.py的os模块导入

在__init__.py顶部添加os模块导入:

import os
from flask import Flask, jsonify
from datetime import timedelta

from flask_sqlalchemy import SQLAlchemy

# 其余代码不变

方案3:利用pytest-dotenv自动加载环境变量

已安装pytest-dotenv插件(测试日志显示plugins: dotenv-0.5.2),可简化操作:

  • 确保.flaskenv文件位于pytest命令执行的根目录
  • 删除手动编写的load_env_vars fixture,插件会自动在测试启动前加载环境变量

额外建议

  • 测试时使用独立的测试数据库,避免污染生产数据
  • 在create_app中添加环境变量缺失检查,提前抛出明确错误:
    def create_app():
        app = Flask(__name__)
        db_uri = os.environ.get("SQLALCHEMY_DATABASE_URI")
        if not db_uri:
            raise ValueError("SQLALCHEMY_DATABASE_URI环境变量未设置")
        app.config["SQLALCHEMY_DATABASE_URI"] = db_uri
        # 其余配置逻辑
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 00:04:54