Testování migračních skriptů s Dockerem
Říjen 2025 — 5 min
SQL migrace jsou kód. Přesto v mnoha týmech zůstávají posledním artefaktem, který se nasazuje na dobré slovo — jednou se napíšou, v review se přelétnou očima a poprvé naostro běží až v produkci, nad reálnými daty. Nejspolehlivější test je přitom stejný jako u čehokoli jiného: proti skutečné databázi, která se chová jako produkce.
Migrace si zaslouží víc podezřívavosti než běžný kód, ne míň. Běží právě jednou, mění stav, který se špatně vrací zpět, a spouštějí se uprostřed deploye — přesně ve chvíli, kdy se všichni dívají a nikdo nestojí o překvapení. Takový rizikový profil volá po nudném, opakovatelném testovacím setupu — a přesně ten Docker nabízí.
Spusťte dočasnou databázi
Docker dělá z izolovaného testování migrací triviální záležitost. Jeden příkaz mi dá čistý Postgres, který žije přesně tak dlouho jako testovací běh:
# Spusťte dočasný Postgres
docker run --rm -e POSTGRES_PASSWORD=pass -p 5432:5432 postgres:16Stejný příkaz běží u mě, u kolegy i v CI — jedno prostředí, nula výmluv. Celý vtip je ve flagu --rm. Kontejner po skončení testu zmizí a každý běh startuje ze známého, prázdného stavu — žádné tabulky z minulého experimentu, žádný drift schématu mezi mým strojem a strojem kolegy. Nad čistou instancí pak přehraju celý řetěz migrací od úplně prvního souboru. Tenhle jediný zvyk odhalí celou třídu chyb: chybějící defaulty sloupců, které se projeví jen na čerstvé instalaci, indexy, které zamknou tabulku na dlouhé minuty, a dotazy, které na deseti řádcích létaly a na deseti milionech se plazí.
Dvě pravidla drží kontejner poctivý. Zaprvé: zafixujte image na přesnou major verzi produkce — postgres:16 nic neznamená, když produkce jede na čtrnáctce, protože DDL, které jedna verze přijme, druhá klidně odmítne. Zadruhé: nahrajte realistická data — anonymizovaný dump nebo generované fixtury v produkčním měřítku. ALTER TABLE nad 200 řádky vám o ALTER TABLE nad 20 miliony neřekne vůbec nic.
Pouštějte stejné kontroly jako v produkci
Lokální test je jen polovina práce. Na mém stroji migrace projde vždycky; zajímá mě, jestli projde i v pátek odpoledne, když ji pouští někdo úplně jiný. Proto kontroly patří do CI, kde poběží na každém pull requestu — bez ohledu na to, jestli si na ně někdo vzpomene. Můj migrační job dělá pět věcí:
- Migrace z nuly: aplikuje všechny migrace popořadě na prázdnou databázi — důkaz, že řetěz pořád funguje pro čerstvé prostředí.
- Migrace z aktuálního stavu: obnoví snapshot produkčního schématu a aplikuje jen nové soubory — důkaz, že diff funguje nad stavem, na kterém skutečně záleží.
- Smoke dotazy: spustí hrstku dotazů, na kterých aplikace stojí, a ověří, že pořád vracejí smysluplné výsledky.
- Rollback: pustí down migraci a smoke dotazy zopakuje. Down migrace tiše hnijí, dokud je nic necvičí.
- Měření času: zaloguje trvání každého příkazu a jeho zámků. Migrace, která na čtyřicet vteřin zablokuje zápisy, není migrace, ale výpadek s razítkem.
Nic z toho není exotika. Je to stejná disciplína, kterou dávno aplikujeme na aplikační kód — automatické testy, reálné závislosti, rychlá zpětná vazba — konečně namířená na jediný skript, který má root přístup k datům. Setup stojí jedno odpoledne; první špatný index, který chytí, ho zaplatí.
Cílem je jistota, ne štěstí. Než se deploy pipeline dostane k migračnímu kroku, ten samý skript už běžel proti stejnému enginu, nad daty produkčního tvaru, v kontejneru, který po sobě nikdo nemusel uklízet. Produkční běh tak přestává být událost a stává se reprízou — a nudné reprízy jsou přesně to, co od svých deployů chcete.