Running Tests

You have two primary options for running the configured set of tests:

  1. Automatically at the command-line via --test-alembic

    Pytest Alembic automatically adds a flag, pytest --test-alembic, which will automatically invoke the baked-in tests.

    This can be convenient if you want to exclude migrations tests most of the time, but include them for e.g. CI. By default, pytest tests would then, not run migrations tests.

    Additionally, it means you don’t need to manually include the tests in a test file somewhere in your project.

    If your tests dont generally reside at/below a tests/ directory with a tests/conftest.py file, you can/should set the pytest_alembic_tests_path option, described below.

  2. You can directly import the tests you want to include at any point in your project.

    tests/test_migrations.py
    from pytest_alembic.tests import (
        test_model_definitions_match_ddl,
        test_single_head_revision,
        test_up_down_consistency,
        test_upgrade,
    )
    

    This can be convenient if you always want the migrations tests to run, or else want a reference to the tests’ existence somewhere in your source code. Pytest would automatically include the tests every time you run i.e. pytest tests.

In either case, you can exclude migrations tests using pytest’s “marker” system, i.e. pytest -m "not alembic".

Configuration

Pytest Config

In any of the pytest config locations (pytest.ini, setup.cfg, pyproject.toml), you can set any of the following configuration options to alter global pytest-alembic behavior.

  • pytest_alembic_include

    List of built-in tests to include. If specified, ‘pytest_alembic_exclude’ is ignored. If both are omitted, all tests are included. The tests should be listed as a comma delimited string containing the tests’ names.

  • pytest_alembic_exclude

    List of built-in tests to exclude. Ignored if ‘pytest_alembic_include’ is specified. The tests should be listed as a comma delimited string containing the tests’ names.

  • pytest_alembic_tests_folder

    The location under which the built-in tests will be bound. This defaults to ‘tests/’ (the tests themselves then being executed from tests/pytest_alembic/), the typical test location. However this can be customized if pytest is, for example, invoked from a parent directory like pytest folder/tests, or the tests are otherwise located at a different location, relative to the pytest invocation.

    Note

    As of pytest-alembic version 0.8.5, this option is ignored. Instead, if you require customizing the registration location, you should use pytest_alembic_tests_path instead.

  • pytest_alembic_tests_path

    Note

    Introduced in v0.10.1.

    The location at which the built-in tests will be bound. This defaults to ‘tests/conftest.py’. Typically, you would want this to coincide with the path at which your alembic_engine is being defined/registered. Note that this path must be the full path, relative to the root location at which pytest is being invoked.

    This option has replaced pytest_alembic_tests_folder due to changes in how pytest test collection needed to be performed in around pytest ~7.0.

    Additionally, this option is only required if you are using the --test-alembic flag.

Alembic Config

See the Config fixture for more detail.

Alternative to --test-alembic

There is some magic to the automatic inclusion of the built-in tests. It’s not obvious, from looking at any of the test code, that these tests (sometimes) magically be included.

Also, one may want to include the built-in tests automatically, every time, without needing to specify --test-alembic, or by doing so conditionally in-code.

Whatever the reason, it is possible to simply import the test implementations from pytest_alembic directly.

Simply import the tests at whatever location you want tests to be included:

tests/test_migrations.py
from pytest_alembic.tests import test_single_head_revision
from pytest_alembic.tests import test_upgrade
from pytest_alembic.tests import test_model_definitions_match_ddl
from pytest_alembic.tests import test_up_down_consistency

Furthermore, doing this as well as using --test-alembic will cause the tests to be run twice (since they’d be considered unique tests with different paths). So generally, these methods should be considered mutually exclusive.

Multiple Alembic Histories

It may be the case that you have the histories for two separate databases (or schemas) in a single project. How should you structure your tests?

This is likely one of the times you want to avoid the use of the --test-alembic flag and the automatic insertion of tests.

Instead, you’ll likely want to want to make use of create_alembic_fixture().

from pytest_alembic import tests, create_alembic_fixture

# The argument here represents the equivalent to `alembic_config`. Depending
# on your setup, this may be configuring the "file" argument, "script_location",
# or some other way of configuring one or the other of your histories.
history_1 = create_alembic_fixture({"file": "alembic.ini"})

def test_single_head_revision_history_1(history_1):
    tests.test_single_head_revision(history_1)

def test_upgrade_history_1(history_1):
    tests.test_upgrade(history_1)

def test_model_definitions_match_ddl_history_1(history_1):
    tests.test_model_definitions_match_ddl(history_1)

def test_up_down_consistency_history_1(history_1):
    tests.test_up_down_consistency(history_1)

# The 2nd fixture, and the 2nd set of tests.
history_2 = create_alembic_fixture({"file": "history_2.ini"})

def test_single_head_revision_history_2(history_2):
    tests.test_single_head_revision(history_2)

def test_upgrade_history_2(history_2):
    tests.test_upgrade(history_2)

def test_model_definitions_match_ddl_history_2(history_2):
    tests.test_model_definitions_match_ddl(history_2)

def test_up_down_consistency_history_2(history_2):
    tests.test_up_down_consistency(history_2)

Due to limitations of how pytest test collection occurs, there’s currently no obvious way to automatically set up and define these tests to occur against different fixtures.

Pytest Marks

Pytest-alembic automatically marks all tests which use the alembic_runner fixture (including all built-in tests) with the alembic mark.

This means you can optionally include/exclude migrations tests using the vanilla pytest mark machinery like so:

pytest -m 'alembic'  # Run *only* alembic tests
pytest -m 'not alembic'  # Run everything *except* alembic tests