Przez lata pisałem testy w ten sposób: zrób feature → napisz testy żeby to sprawdzić. Test jako potwierdzenie, że kod robi to, co myślę że robi.
W listopadzie 2025, przy implementacji testów E2E dla geo-auth, zmieniłem podejście. Kilkadziesiąt testów napisanych w jednym tygodniu — ale każdy z nich zaczął się od specyfikacji reguły biznesowej, nie od kodu.
Skąd pomysł
Geo-auth to kontekst o skomplikowanych regułach biznesowych: kiedy można zmienić adres, co jest wymagane do każdego poziomu weryfikacji, jak uprawnienia zmieniają się przy przeprowadzce.
Te reguły były rozproszone po dokumentach — zadaniach, decyzjach architektonicznych, plikach specyfikacji. Ale kiedy pisałem test, i tak musiałem pamiętać, która reguła co mówi. I test sprawdzał, czy kod robi to, co ja rozumiem — nie to, co specyfikacja mówi.
Zmiana: każdy test dostał identyfikator reguły biznesowej jako komentarz. Reguła z dokumentu → test → kod. Trójka połączona.
Co konkretnie się zmieniło
Przed listopadem:
test('should verify user address')
→ implementuję, co myślę że test powinien sprawdzać
→ sprawdzam, czy kod przechodzi
Po zmianie:
// reguła: po zmianie adresu nie dziedziczysz uprawnień ze starego
test: zweryfikowany mieszkaniec zmienia adres → próba akcji „lokalnej"
na starej dzielnicy → powinno fail (uprawnienia wygasły)
implementacja: reset uprawnień przy zmianie adresu
test przechodzi
Różnica jest subtelna, ale kluczowa: w pierwszym przypadku testuję implementację. W drugim testuję regułę. Implementacja może się zmienić — reguła nie.
Kilkadziesiąt testów w jeden tydzień
Kiedy masz reguły zdefiniowane w specyfikacji, pisanie testów E2E staje się mechaniczne: każda reguła → jeden test (lub kilka przypadków brzegowych).
W połowie listopada napisałem w tydzień kilkadziesiąt testów E2E dla geo-auth. Nie dlatego, że byłem szczególnie produktywny — ale dlatego, że wiedziałem dokładnie, co każdy test ma sprawdzać.
Specyfikacja-first okazała się też dobrym filtrem na przeinżynierowanie: jeśli nie ma reguły biznesowej, nie ma testu. Jeśli nie ma testu — może nie ma potrzeby tej funkcji?