Pytest autoloaded models

585 views Asked by At

I've setup my Flask app using factories, e.g. I have create_app() method which creates and returns app. SQLAlchemy initialization is done during this process too. (and I'm using Flask-SQLAlchemy). I used autoload functionality of SQLAlchemy to load models from DB.

class User(db.Model):
    __table__ = db.Table('users', db.metadata, autoload=True, autoload_with=db.engine)

Application works perfectly, only thing I needed to do is push application context before initializing: app.app_context().push()

Now, I'm trying to setup pytest with factory_boy to test API endpoints. pytest works fine with preloaded data in DB. However when I'm trying to setup factory_boy with current model definition, I'm getting RuntimeError: application not registered on db instance and no applicationbound to current context error. Here's the conftest file

import pytest

from core.factories import create_app
from core.extensions import db as _db


@pytest.yield_fixture(scope='session')
def app():
    app = create_app(__name__, 'testing')
    ctx = app.app_context()
    ctx.push()
    yield app
    ctx.pop()


@pytest.yield_fixture(scope='session')
def db(app):
    _db.create_all()
    yield _db
    _db.drop_all()


@pytest.fixture(scope='session')
def client(app):
    return app.test_client()


@pytest.yield_fixture(scope='function')
def session(db):
    connection = db.engine.connect()
    transaction = connection.begin()

    options = dict(bind=connection)
    session = db.create_scoped_session(options=options)

    db.session = session

    yield session

    transaction.rollback()
    connection.close()
    session.remove()

And factory_boy factories

import factory
from faker import Factory as FakerFactory

from core.extensions import db
from database.models import User

faker = FakerFactory.create()


class SQLAlchemyModelFactory(factory.Factory):

    class Meta:
        abstract = True

    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        session = db.session
        session.begin(nested=True)
        obj = model_class(*args, **kwargs)
        session.add(obj)
        session.commit()
        return obj


class UserFactory(SQLAlchemyModelFactory):

    class Meta:
        model = User

    id = factory.LazyAttribute(lambda x: faker.uuid4())
    name = factory.LazyAttribute(lambda x: faker.name())

And simple test

from uuid import uuid4

from tests.factories import UserFactory
from database.models import UserSchema


class TestUserController:
    def test__get_users(self, client, session):
        user = UserFactory.create(id=str(uuid4()), name='John')
        schema = UserSchema()
        res = client.get('/api/users')
        assert res.status_code == 200
        assert res.json == [schema.dump(user).data]

And this is the error trace

tests/test_controllers.py:3: in <module>
    from tests.factories import UserFactory
tests/factories.py:5: in <module>
    from database.models import User
database/models.py:4: in <module>
    class User(db.Model):
database/models.py:6: in User
    autoload=True, autoload_with=db.engine)
../../.virtualenvs/flask-boilerplate/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py:922: in engine
    return self.get_engine()
../../.virtualenvs/flask-boilerplate/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py:931: in get_engine
    app = self.get_app(app)
../../.virtualenvs/flask-boilerplate/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py:957: in get_app
0

There are 0 answers