I am writing a test for my Flask app that uses Flask-SQLAlchemy. In models.py
, I used db = SQLAlchemy()
, and wrote a function to configure it with the app. But when I run my test, I get the error "RuntimeError: A 'SQLAlchemy' instance has already been registered on this Flask app". I'm not sure where the test file is creating a new instance of SQLAlchemy.
# flaskr.py
from flask import Flask
from models import setup_db
def create_app(test_config=None):
app = Flask(__name__)
setup_db(app)
return app
# models.py
from flask_sqlalchemy import SQLAlchemy
database_path = "postgresql://student:student@localhost/bookshelf"
db = SQLAlchemy()
def setup_db(app, database_path=database_path):
app.config["SQLALCHEMY_DATABASE_URI"] = database_path
db.init_app(app)
with app.app_context():
db.create_all()
# test_flaskr.py
import unittest
from flaskr import create_app
from models import setup_db
class BookTestCase(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.client = self.app.test_client()
setup_db(self.app, "postgresql://student:student@localhost/bookshelf_test")
with self.app.app_context():
self.db = SQLAlchemy()
self.db.init_app(self.app)
self.db.create_all()
def test_get_paginated_books(self):
res = self.client.get("/books")
data = res.json
self.assertEqual(res.status_code, 200)
self.assertTrue(data["success"])
self.assertTrue(data["total_books"])
self.assertTrue(len(data["books"]))
When I run the test, I get the following error:
$ python -m unittest -v test_flaskr.py
test_get_paginated_books (test_flaskr.BookTestCase) ... ERROR
======================================================================
ERROR: test_get_paginated_books (test_flaskr.BookTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\swilk\OneDrive\DOCS-Programming\udacity-demo-bookshelf\backend\test_flaskr.py", line 21, in setUp
setup_db(self.app, self.database_path)
File "C:\Users\swilk\OneDrive\DOCS-Programming\udacity-demo-bookshelf\backend\models.py", line 24, in setup_db
db.init_app(app)
File "C:\Users\swilk\OneDrive\DOCS-Programming\udacity-demo-bookshelf\env\lib\site-packages\flask_sqlalchemy\extension.py", line 253, in init_app
raise RuntimeError(
RuntimeError: A 'SQLAlchemy' instance has already been registered on this Flask app. Import and use that instance instead.
----------------------------------------------------------------------
Ran 1 tests in 0.226s
FAILED (errors=1)
Flask-SQLAlchemy 3 raises an error for a common incorrect setup of the extension. You must only call db.init_app(app)
once for a given pair of db
and app
instances.
You defined db = SQLAlchemy()
in models
, then called init_app
on it in create_app
. You must use that db
, not create another instance. You must not call db.init_app
again, it was already called in create_app
.
Your test's setUp
is calling create_app
, but then it's also calling setup_db
again even though that's already called as part of create_app
. Then further down, you're creating a new self.db = SQLAlchemy()
and calling init_app
on it with the same app you've already set up the other instance on. This is all incorrect.
It looks like you're doing this because you're trying to set a database configuration for the test. As explained in the Flask tutorial, that's what the create_app
function's test_config
parameter is for. Get rid of the setup_db
function, it's unnecessary indirection. Pass your test configuration to the factory to override default config.
def create_app(test_config=None):
app = Flask(__name__)
app.config.from_mapping(
SQLALCHEMY_DATABASE_URI="postgresql://student:student@localhost/bookshelf"
)
if test_config is not None:
app.config.from_mapping(test_config)
db.init_app(app)
with app.app_context():
db.create_all()
return app
from flaskr import create_app, db
class BookTestCase(TestCase):
def setUp(self):
self.app = create_app({
"SQLALCHEMY_DATABASE_URI": "postgresql://student:student@localhost/bookshelf_test"
})
Note that nothing was done directly with db
in setUp
, it was only imported to be used during tests.