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"
问题原因
- 环境变量加载时机滞后:测试文件中先导入
backend.app,再通过fixture加载环境变量。而app = create_app()在导入时就会执行,此时os.environ还未读取.flaskenv中的变量,导致SQLALCHEMY_DATABASE_URI被设置为None,触发SQLAlchemy的初始化检查错误。 __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_varsfixture,插件会自动在测试启动前加载环境变量
额外建议
- 测试时使用独立的测试数据库,避免污染生产数据
- 在
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
相关产品推荐
相关产品推荐

