pytest-mrt
Migration Rollback Tester — A pytest plugin that catches database migration disasters before they reach production.
The problem
You run alembic downgrade -1. It succeeds. No errors.
But your users' data is gone.
The column structure came back. The rows didn't.
This is one of the most common sources of production incidents with database migrations. alembic downgrade can succeed while silently destroying everything it was supposed to restore — because most tools only check that the migration runs, not that your data survives.
How pytest-mrt helps
pytest-mrt does two things:
1. Static analysis — scans your migration files for 24 known dangerous patterns before you touch a database:
┌──────────┬─────────────────────────┬───────┬──────────────────────────────────────┐
│ Revision │ Pattern │ Sev │ Message │
├──────────┼─────────────────────────┼───────┼──────────────────────────────────────┤
│ 004 │ DROP COLUMN in upgrade │ error │ Data permanently lost on rollback │
│ 005 │ No-op downgrade │ error │ downgrade() does nothing │
└──────────┴─────────────────────────┴───────┴──────────────────────────────────────┘
2 error(s)
2. Dynamic verification — seeds real data, runs your migration, rolls it back, and checks nothing was lost:
✓ 001 reversible
✓ 002 reversible
✗ 003 data loss detected
└─ Table 'users': 3/3 rows lost after rollback
FAILED
Install
Because it's a pytest plugin, the mrt fixture is automatically available in all your tests once installed. No imports needed in test files.
5-minute setup
1. Install:
2. Create 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",
)
3. Write a test:
4. Run:
Supported databases
| Database | Static analysis | Dynamic verification |
|---|---|---|
| PostgreSQL | ✅ | ✅ |
| SQLite | ✅ | ✅ |
| MySQL / MariaDB | ✅ | 🔜 planned |
Why not just read the migration file manually?
You could — but you'd have to know all 24 patterns, remember to check every PR, and still wouldn't catch the cases that only appear when real data is present. pytest-mrt does it automatically on every test run.