1
votes

I have a project and I've defined my db.py module as:

app = get_global_flask_app()
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://foo:[email protected]:5432/test"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
db.create_all()

Then I import db.db into modules that need to query the database and insert data (db.session.query()).

However, this means that when I write test code (pytest) to test any module that imports db.py, I will need to define "SQLALCHEMY_DATABASE_URI". One solution is to have db be a lazy attribute so that the code above is executed in tests only if the database is used/tested. Is there a common design pattern for Flask() + SQLA + SQLALCHEMY_DATABASE_URI out there that I'm missing? How would you solve this problem? Flask-config?

1

1 Answers

2
votes

The way we normally solve this problem is with an application factory and a config.

This means you have a function somewhere in your project that looks something like this (taken from the documentation with modifications):

def create_app(config_filename, settings_override=None):
    app = Flask(__name__)
    app.config.from_pyfile(config_filename)
    app.config.from_object(settings_override)

    from yourapplication.model import db
    db.init_app(app)

    from yourapplication.views.admin import admin
    from yourapplication.views.frontend import frontend
    app.register_blueprint(admin)
    app.register_blueprint(frontend)

    return app

Then (and hopefully you're using pytest) in your root test directory, you have a conftest file which automatically prepares your test environment something like this:

import pytest
from your_project import create_app


class TestConfig:
    SQLALCHEMY_DATABASE_URI = "postgresql://foo:[email protected]:5432/test"
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    ANY OTHER SETTINGS...


@pytest.fixture(autouse=True)
def app(request):
    app = create_app(settings_override=TestConfig)
    ctx = app.app_context()
    ctx.push()

    def teardown():
        ctx.pop()

    request.addfinalizer(teardown)

    return app

Typically, we create another fixture which is also autouse=True that handles that DB set-up, flush, and possibly loading fixtures, and only use that fixture in tests which need to access the DB (integration or functional tests), which means simply that we include in a conftest file in the same directory as our integration tests.