jeancochrane / pytest-flask-sqlalchemy

A pytest plugin for preserving test isolation in Flask-SQLAlchemy using database transactions.
MIT License
254 stars 45 forks source link

Incompatible with flask-sqlalchemy-3.0 #63

Open stabacco opened 2 years ago

stabacco commented 2 years ago

Looks like there has been a new major release for flask-sqlalchemy https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/changes/#version-3-0-0 and they have broken backwards compatibility in a few places.

For example :

    @pytest.fixture(scope='function')
    def _transaction(request, _db, mocker):
        '''
        Create a transactional context for tests to run in.
        '''
        # Start a transaction
>       connection = _db.engine.connect()

../../../.cache/pypoetry/virtualenvs/api-S2mtXI53-py3.8/lib/python3.8/site-packages/pytest_flask_sqlalchemy/fixtures.py:31: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../../.cache/pypoetry/virtualenvs/api-S2mtXI53-py3.8/lib/python3.8/site-packages/flask_sqlalchemy/extension.py:642: in engine
    return self.engines[None]
../../../.cache/pypoetry/virtualenvs/api-S2mtXI53-py3.8/lib/python3.8/site-packages/flask_sqlalchemy/extension.py:628: in engines
    app = current_app._get_current_object()  # type: ignore[attr-defined]
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    def _get_current_object() -> T:
        try:
            obj = local.get()  # type: ignore[union-attr]
        except LookupError:
>           raise RuntimeError(unbound_message) from None
E           RuntimeError: Working outside of application context.
E           

Also the create_scoped_session method seems to have gone.

zoltan-fedor commented 2 years ago

Yeap, create_scoped_session method has been replaced by _make_scoped_session, see https://github.com/pallets-eco/flask-sqlalchemy/blob/2908083d0d0c918e1adce396d351da6fa3112692/src/flask_sqlalchemy/extension.py#L336, so that would need to be updated to that.

Midnighter commented 1 year ago

I believe a solution is to use SQLAlchemy directly:


from sqlalchemy.orm import sessionmaker, scoped_session

db.session = scoped_session(session_factory=sessionmaker(bind=db_connection))
pamelafox commented 1 year ago

Thanks @Midnighter ! If folks are looking for a workaround for now, this is the conftest.py I'm using with latest flask-sqlalchemy for similar purposes - not as comprehensive as this package, but works for a fake session:

https://github.com/pamelafox/flask-surveys-container-app/blob/main/backend/tests/conftest.py

davidism commented 1 year ago

I'm not sure exactly how this library works, but the way config, engines, and the session are managed did change significantly in Flask-SQLAlchemy 3.0. Just monkeypatching db.session as suggested above might work for simple cases, but I have a feeling it will fail in weird ways.

The following code sets up transactions for each bind, then rolls them back after each test. This supports multiple binds, in case one request queries multiple databases.

@pytest.fixture
def db(app):
    with app.app_context():
        engines = db.engines

    cleanup = []

    for key, engine in engines.items():
        c = engine.connect()
        t = c.begin()
        engines[key] = c
        cleanup.append((key, engine, c, t))

    yield db

    for key, engine, c, t in cleanup:
        t.rollback()
        c.close()
        engines[key] = engine
jasondoong commented 11 months ago

I'm not sure if this works in more complex situations, but it works in my project. https://github.com/Teachesworkspace/pytest-flask-sqlalchemy/commit/d6ae3d9e9292f2a61a3aa5bc988114a6ce36bf56

gmassman commented 10 months ago

If anyone was like me and had problems migrating pytest from SQLAlchemy <= 1.4 to SQLAlchemy 2.0, I created a minimum working example and was able to backtrack through my own test suite to find out why transactional tests weren't working.

https://github.com/gmassman/fsa-rollback-per-test-example