Flask-migrate: How to test migrations?

Created on 3 Jul 2020  路  8Comments  路  Source: miguelgrinberg/Flask-Migrate

I am trying to get testing going for my migrations, and had a look at pytest-alembic, but I had difficulties getting it going.

  • If there is a known way on how to test it, it should be documented.
  • If it is and I didn't find it I'm sorry. In that case, I propose to add it to the table of contents on the left side of https://flask-migrate.readthedocs.io/en/latest/
  • If there is no known way, we should find one :)
question

All 8 comments

Good question! Thanks for alerting me of the pytest-alembic, didn't know that project.

I sort of always implement migration tests manually, but I spent a few minutes trying to get the pytest-alembic tests to work. I had to add the following conftest.py to my project to adapt the defaults in pytest-alembic to Flask-Migrate:

import pytest


@pytest.fixture
def alembic_config():
    """Override this fixture to configure the exact alembic context setup required.
    """
    return {'file': 'migrations/alembic.ini'}


@pytest.fixture
def alembic_runner(alembic_config, alembic_engine):
    """Produce an alembic migration context in which to execute alembic tests.
    """
    import pytest_alembic

    from app import create_app   # <--- this line and the next are dependent on your app structure
    app = create_app()

    with app.app_context():
        with pytest_alembic.runner(config=alembic_config,
                                   engine=alembic_engine) as runner:
            yield runner

This makes two changes. First, it tells pytest-alembic that the alembic.ini file is inside the migrations directory (Alembic likes to put it in the root folder of the project). The second change is to push a Flask application context when creating a migration context. This is required because Flask-Migrate uses the db object from Flask-SQLAlchemy to pass schema information to Alembic.

With these changes I was able to get 3 of the 4 tests from pytest-alembic passing. The test that does up/down every migration fails for me because I have the tests on this app I'm using configured to use sqlite, so the first DROP COLUMN it blows up. It should work fine on MySQL/Postgres, or if you enable batch mode.

Hope this helps!

@miguelgrinberg Nice work! This has been troublesome for me with writing test cases (i'm using Flask-SQLAlchemy & Flask-Migrate for db migrations). How should alembic_engine be defined as in your code example above?

@elizagraham the alembic_engine fixture is not used in any of the four tests that come predefined with this plugin, so I did not need to do anything with it. Normally when you work with a Flask application you will not get a separate database engine, you will use the Flask-SQLAlchemy one.

@miguelgrinberg thanks for the explanation. Within your set up how have you configured the env.py file that gets generated in your migrations folder?

@elizagraham I did not need to change anything in the migrations directory, I just added the conftest.py file shown above. Something you need to have is a testing configuration for your Flask app, because you don't want the tests to use your development database. The application that I used to test this had this in place already.

@miguelgrinberg yes that's what i'm trying to set up. I'm relatively new to python and flask and I'm trying to understand how to best setup the testing configuration for my Flask app. Currently within my app i can run flask db migrate and flask db upgrade to run migrations on the main database and that works.

There are a few ways to do this. For this exercise I used my Mega-Tutorial app, which overrides the Config class during testing: https://github.com/miguelgrinberg/microblog/blob/master/tests.py#L17.

@miguelgrinberg okay i will check that out thanks :)

Was this page helpful?
0 / 5 - 0 ratings