Flask单元测试未使用指定测试数据库,仍连接生产数据库的问题排查
Flask单元测试未使用指定测试数据库,仍连接生产数据库的问题排查
嘿,我看了你的问题,核心原因其实很明确:你修改数据库配置的时机太晚了!
你看,在你的测试文件里,你先执行了from app import app和from models import db, Pet,这时候app.py里的代码已经全部执行了——包括connect_db(app)和db.create_all(),这意味着数据库已经和生产库postgresql:///petagency建立了连接,之后你再修改app.config['SQLALCHEMY_DATABASE_URI']根本不会生效,因为连接已经在导入阶段就建立好了。
而CSRF配置生效是因为Flask-WTF的CSRF检查是在处理请求时动态读取配置的,不是在app初始化时就固定的,所以你之后修改的配置能被读取到。
下面给你两个可行的解决方案:
方案一:使用Flask应用工厂函数(推荐)
这是Flask项目的最佳实践,能彻底解决这类环境配置冲突的问题。你需要重构app.py,把应用初始化逻辑封装到一个函数里,这样测试时可以先传入测试配置,再创建应用:
修改app.py:
from flask import Flask, render_template, redirect, flash from models import db, connect_db, Pet from forms import AddPetForm, EditPetForm from flask_wtf import FlaskForm from wtforms import StringField, IntegerField, BooleanField, TextAreaField from flask_debugtoolbar import DebugToolbarExtension from sqlalchemy import exc from werkzeug.utils import secure_filename from flask_wtf.file import FileField, FileAllowed def create_app(config_overrides=None): # 创建Flask实例 app = Flask(__name__) # 设置默认配置 app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:///petagency' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_ECHO'] = True app.config['SECRET_KEY'] = "secret" app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False # 如果有传入测试配置,覆盖默认配置 if config_overrides: app.config.update(config_overrides) # 初始化调试工具栏 DebugToolbarExtension(app) # 连接数据库 connect_db(app) # 这里可以注册你的路由(如果之前路由是直接写在app.py里的,要移到这里) return app # 只有当直接运行app.py时,才初始化应用并启动服务 if __name__ == "__main__": app = create_app() with app.app_context(): db.create_all() app.run()
修改测试文件test_flask.py:
from unittest import TestCase from app import create_app from models import db, Pet class PetAgencyTestCase(TestCase): def setUp(self): # 创建带测试配置的应用 self.app = create_app({ 'SQLALCHEMY_DATABASE_URI': 'postgresql:///petagency_test', 'SQLALCHEMY_ECHO': False, 'TESTING': True, 'WTF_CSRF_ENABLED': False }) # 创建测试客户端 self.client = self.app.test_client() # 在应用上下文里初始化测试数据库 with self.app.app_context(): db.drop_all() db.create_all() def tearDown(self): # 测试结束后清理数据库 with self.app.app_context(): db.session.remove() db.drop_all() # 在这里写你的测试用例,比如: def test_add_pet(self): with self.client as c: resp = c.post('/add', data={ 'name': 'TestPet', 'species': 'Dog', 'age': 3 }) self.assertEqual(resp.status_code, 302) with self.app.app_context(): pet = Pet.query.filter_by(name='TestPet').first() self.assertIsNotNone(pet)
方案二:临时修复(不推荐,仅用于快速验证)
如果暂时不想重构代码,你可以在测试文件里修改配置后,重新初始化数据库连接:
from unittest import TestCase from app import app from models import db, Pet # 先修改配置 app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:///petagency_test' app.config['SQLALCHEMY_ECHO'] = False app.config['TESTING'] = True app.config['WTF_CSRF_ENABLED'] = False # 重新初始化数据库连接 db.init_app(app) # 在应用上下文里重建测试库 with app.app_context(): db.drop_all() db.create_all() class PetAgencyTestCase(TestCase): # 测试用例...
这个方法的缺点是,app.py在导入时还是会先连接一次生产数据库,可能会产生不必要的副作用,所以还是推荐用应用工厂的方案。
备注:内容来源于stack exchange,提问作者Camden Tadhg
相关产品推荐
相关产品推荐

