I am configuring a fixture for pytest that creates a flask app instance. My app is created with Application Factories pattern. I am at the stage of connecting it to the database and struggle to understand the difference between 2 patterns.
# project/__init__.py
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app_settings = os.getenv('APP_SETTINGS')
app.config.from_object(app_settings)
db.init_app(app)
[blueprint code]
return app
In my fixture, I think I understand the need for:
db.create_all()during setup: create my tablesdb.drop_all()during teardown: clean database after testsdb.session.remove()during teardown: avoid some weird locks on postgres when hitting the database frequently in tests
The first setup (inspired by Miguel Grinberg book) makes sense to me:
import pytest
from project import create_app, db
@pytest.fixture
def app():
app = create_app()
with app.app_context():
db.create_all()
yield app
db.session.remove()
db.drop_all()
It also matches the behavior I get in an interactive session, where I need to activate/push the app_context to bind the database:
Python 3.6.1 (default, Jun 21 2017, 18:45:41)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from project import create_app, db
>>> app = create_app()
>>> db
<SQLAlchemy engine=None>
>>> app_ctx = app.app_context()
>>> app_ctx.push()
>>> db.create_all()
>>> db
<SQLAlchemy engine='postgres://postgres:postgres@users-db:5432/users_dev'>
The second setup (inspired by testdriven.io) also work in pytest but I don't know why:
import pytest
from project import create_app, db
@pytest.fixture
def app():
app = create_app()
db.create_all()
db.session.commit() # fail when this is removed
yield app
db.session.remove()
db.drop_all()
Actually, if I try to do the same in an interactive session, I get an error:
Python 3.6.1 (default, Jun 21 2017, 18:45:41)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from project import create_app, db
>>> app = create_app()
>>> db.create_all()
[...] timeError: application not registered on db instance and no application bound to current context
I tried to run the fixture without db.session.commit(), thinking that maybe I was in the app context by default (similar to what I do with with app_context() in the first fixture). But it fails if I remove it.