Troubleshooting
Setup errors
AttributeError: 'Config' object has no attribute '_mrt_config'
Cause: conftest.py is missing or the pytest_configure function isn't set up.
Fix: Create conftest.py in your project root (where you run pytest from):
# conftest.py
from pytest_mrt import MRTConfig
def pytest_configure(config):
config._mrt_config = MRTConfig(
alembic_ini="alembic.ini",
db_url="postgresql://localhost/myapp_test",
)
Make sure pytest_configure is a top-level function, not inside a class.
FileNotFoundError: alembic.ini not found
Cause: The path to alembic.ini is wrong relative to where you run pytest.
Fix: Run pytest from the directory that contains alembic.ini, or use an absolute path:
import os
MRTConfig(
alembic_ini=os.path.join(os.path.dirname(__file__), "alembic.ini"),
db_url="...",
)
could not connect to server: Connection refused
sqlalchemy.exc.OperationalError: (psycopg2.OperationalError)
could not connect to server: Connection refused
Cause: The database server isn't running, or the URL is wrong.
Fix:
# Check PostgreSQL is running
pg_isready -h localhost
# Test the connection manually
psql postgresql://localhost/myapp_test
# Check the URL format
# postgresql://user:password@host:port/dbname
# sqlite:///path/to/file.db (relative)
# sqlite:////absolute/path.db (absolute, 4 slashes)
Target database is not up to date
Cause: pytest-mrt starts from alembic downgrade base, but Alembic's base state requires the migration history to be clean.
Fix: Make sure your test database has no leftover state from previous test runs. Add to your conftest.py:
from pytest_mrt import MRTConfig
from sqlalchemy import create_engine, text
def pytest_configure(config):
db_url = "postgresql://localhost/myapp_test"
# Drop and recreate the test database before each run
engine = create_engine(db_url)
with engine.connect() as conn:
conn.execute(text("DROP SCHEMA public CASCADE"))
conn.execute(text("CREATE SCHEMA public"))
engine.dispose()
config._mrt_config = MRTConfig(alembic_ini="alembic.ini", db_url=db_url)
Test failures
Table 'X' no longer exists after rollback
Cause: The migration drops a table in upgrade() and doesn't recreate it in downgrade(), OR downgrade() is a no-op that doesn't reverse a CREATE TABLE.
Which migration is it? Run mrt check to find it immediately:
Common fix for CREATE TABLE with no-op downgrade:
X/Y rows lost after rollback
Cause: The migration drops a column or truncates data in upgrade(). The schema comes back in downgrade(), but the actual row data was destroyed.
This is the most important thing pytest-mrt catches. The migration passes alembic downgrade but leaves your data in a bad state.
Which column? Look for op.drop_column() in the migration's upgrade() function. The static analysis will have caught it too:
Column 'X' value changed after rollback
FAILED: Table 'users': column 'status' value changed after rollback
(expected 'active', got 'inactive')
Cause: The downgrade() function modifies data incorrectly — it doesn't fully restore the pre-migration state.
Fix: Check your downgrade() UPDATE statements. The values after rollback must exactly match what existed before the migration ran.
Table 'X' still exists after rollback — downgrade is incomplete
Cause: The migration creates a table in upgrade() but downgrade() is pass or empty.
Fix:
Column 'X' still present after rollback — downgrade is incomplete
Cause: The migration adds a column in upgrade() but doesn't remove it in downgrade().
Fix:
Static analysis
A pattern is flagged but my migration is intentional
Some patterns are warnings, not errors. If your migration intentionally drops a column (for example, after a confirmed data migration), you can:
- Acknowledge it — the warning tells reviewers to double-check the PR
- Run with
--strictonly on errors — omit--strictto let warnings pass CI
There is no per-migration skip/ignore mechanism yet — it's on the roadmap.
mrt check reports Multiple heads
Cause: Two developers created migrations independently from the same parent revision.
Fix:
This creates a merge migration that resolves the conflict.
False positive on op.execute()
op.execute() is flagged as a warning when the downgrade() function has no corresponding op.execute(). If both upgrade() and downgrade() have op.execute(), no warning is raised.
Performance
Tests are slow
check_all() runs downgrade base → upgrade revision N for each revision to ensure clean state. For large migration histories (100+ revisions), this can be slow.
Options:
-
Test only recent migrations in CI:
-
Run
check_all()on a schedule (nightly) rather than on every PR. -
Use SQLite for local development — SQLite is much faster than PostgreSQL for migration testing.
Getting help
If you've hit something not covered here, open an issue with:
- The migration file that's causing the problem
- The full error output
- Your database type and version
- Your pytest-mrt version (
mrt version)