olirice / alembic_utils

An alembic/sqlalchemy extension for migrating sql views, functions, triggers, and policies
MIT License
193 stars 42 forks source link

Document the equivilent of #132

Closed jankatins closed 1 month ago

jankatins commented 1 month ago

In tests, I use Base.metadata.create_all(sqla_engine) in fixtures to have a fast way to create throwaway DBs (via testcontainers and one created DB er function) to run tests on. But now I need to create some replaceable entities first and I'm missing these entities. What is the equivalent function to do this?

Currently I'm doing the equivilent of


    with sqla_engine.begin() as conn:
        for entity in data_sqlalchemy_models.replaceable_entities:
            conn.execute(entity.to_sql_statement_create())
``
olirice commented 1 month ago

alembic_utils is all about migrations so we don't have a pre-built function as an escape hatch like you're describing

If your replaceable entities don't have any dependencies among them, what you have written above would work.

imo its worth configuring your test suite to create your database using the migrations as that guarantees you the most reproducible environment.

Here's the template I use:

# <your_project>/testbase/alembic.py
from typing import Callable, Dict, List
from alembic import command as alembic_command
from alembic.config import Config

COMMAND_MAP = {
    "upgrade": alembic_command.upgrade,
}

def get_alembic_config(engine, path="alembic.ini"):
    """alembic configuration with overrides"""
    alembic_cfg = Config(path)
    # Override the sqla url with the test database
    alembic_cfg.set_main_option("sqlalchemy.url", str(engine.url))
    # configure your migrations directory and other settings as needed
    alembic_cfg.set_main_option("script_location", "migrations")
    return alembic_cfg

def run_alembic_command(engine, command: str, command_args, **command_kwargs):
    command_func = COMMAND_MAP[command]

    alembic_cfg = get_alembic_config(engine)
    with engine.begin() as connection:
        alembic_cfg.attributes["connection"] = connection
        return command_func(alembic_cfg, *command_args, **command_kwargs)

And then in the test suite create where you configure a SQLA engine fixture, run the migrations up

@pytest.fixture(scope="session")
def engine():
    eng = create_engine("<your_test_db_connection_str>")
    run_alembic_command(engine=eng, command="upgrade", command_args=["head"])
    yield eng
    eng.dispose()

instead of relying on create_all