Flaky testy - Jak naprawić niestabilne testy i im zapobiec?

Kazimierz Sadowski .

11 marca 2026

Flaky Tests co to jest?" - pytanie o nieprzewidywalne testy, które czasem przechodzą, a czasem nie.

Niestabilny test to jeden z tych problemów, które potrafią zepsuć cały rytm pracy zespołu. Raz przechodzi, raz pada, mimo że kod się nie zmienił, więc zamiast sygnału dostajesz szum i fałszywe alarmy. W praktyce traktuję taki flaky test jak dług techniczny: nie tylko spowalnia CI, ale też osłabia zaufanie do całej automatyzacji.

Najkrócej, problem zwykle leży w izolacji, czasie albo środowisku

  • Niestabilny test nie jest „kapryśny” bez powodu, tylko najczęściej traci kontrolę nad zależnością, danymi albo momentem wykonania.
  • Retry w CI może pomóc przetrwać dzień, ale nie usuwa przyczyny.
  • Najpierw porównuję udany i nieudany przebieg, potem zawężam zakres i eliminuję źródło losowości.
  • W testach UI najczęściej winne są animacje, opóźnienia sieci, zbyt krótkie czekanie albo współdzielony stan.
  • Dobre narzędzia diagnostyczne pomagają, ale nie zastępują stabilnej konstrukcji testu.

Czym jest niestabilny test i dlaczego nie wolno go bagatelizować

Niestabilny test to taki, który przy tym samym wejściu i bez zmian w kodzie raz kończy się sukcesem, a raz porażką. To nie jest zwykły błąd asercji ani „pechowy run”, tylko sygnał, że test nie jest w pełni deterministyczny, czyli nie daje tego samego wyniku w przewidywalnych warunkach.

Jak trafnie pisze Martin Fowler, takie testy zwykle pokazują brak kontroli nad zależnością istotną dla wyniku. I właśnie dlatego nie traktuję ich jak drobnej niedogodności. Jedno niestabilne sprawdzenie potrafi uruchomić całą lawinę: ludzie odpalają je ponownie „na wszelki wypadek”, ignorują prawdziwe regresje albo przestają ufać całemu pipeline’owi.

Największy koszt nie zawsze widać w samym czasie wykonania. Często większym problemem jest utrata wiarygodności: test zaczyna być postrzegany jako hałas, a nie jako kontrola jakości. Gdy do tego dochodzą spowolnienia w CI i zbędne ręczne powtórki, zespół zaczyna płacić za jeden objaw kilka razy. Żeby to naprawić sensownie, najpierw trzeba rozpoznać, skąd bierze się niestabilność.

Skąd bierze się niestabilność w testach

W praktyce źródło problemu najczęściej nie jest jedno. Zwykle nakładają się dwa albo trzy czynniki, które osobno jeszcze „jakoś działają”, ale razem tworzą losowość.

  • Race condition - test sprawdza stan zanim aplikacja naprawdę skończyła pracę. Typowy przykład to UI, które czeka na dane z API albo zadanie w tle.
  • Współdzielony stan - ta sama baza, ten sam użytkownik testowy, ten sam katalog tymczasowy albo ten sam cache używany przez wiele uruchomień.
  • Zależności zewnętrzne - API, sieć, kolejki, baza, usługa uwierzytelniania. Jeśli coś spoza procesu testu bywa wolne lub niedostępne, wynik też będzie chwiejny.
  • Różnice między lokalnie a CI - inne zasoby, inne obciążenie, inna kolejność uruchomień, czasem też inna przeglądarka lub inna wersja biblioteki.
  • Zbyt sztywne założenia czasowe - test zakłada, że animacja, render albo zapis do bazy zakończy się „dość szybko”, ale nie mierzy warunku końcowego.
  • Zbyt szeroki zakres - test E2E sprawdza za dużo naraz, więc trudno ustalić, co faktycznie zawiodło.

W dokumentacji narzędzi testowych te same grupy problemów wracają bardzo konsekwentnie: animacje, wywołania API, dostępność serwera lub bazy, zależności zasobów i sieć. To dobry znak, bo oznacza, że nie walczymy z egzotyką, tylko z powtarzalnym zestawem błędów projektowych. Następny krok to odróżnienie objawu od źródła, bo podobny fail może wynikać z zupełnie innych przyczyn.

Czerwone krzyżyki nad napisem

Jak rozpoznać, że problem leży w teście, a nie w kodzie

Ja zaczynam od jednego pytania: czy ten sam commit daje ten sam wynik po kilku uruchomieniach w identycznym środowisku? Jeśli nie, to zwykle mam do czynienia z niestabilnością, a nie z czystym błędem funkcjonalnym. Jeśli tak, a fail pojawia się konsekwentnie, bardziej podejrzewam regresję w aplikacji.

Objaw Co to zwykle sugeruje Co sprawdzam najpierw
Test pada tylko czasami na tym samym commicie Losowość, timing albo zależność od stanu Logi, trace, retry i to, czy test czeka na warunek, a nie na czas
Test przechodzi lokalnie, a pada w CI Różnice środowiskowe, zasoby, dane lub kolejność uruchomień Obciążenie maszyny, wersje, równoległość, dane testowe
Test pada tylko przy uruchomieniu równoległym Współdzielony stan lub brak izolacji Baza, konta testowe, pliki tymczasowe, cache
Test działa w jednej przeglądarce, a w drugiej nie Różnice renderowania lub problem z selektorami Selektory, asynchroniczne renderowanie, zależne od przeglądarki zachowanie UI

W takich sytuacjach oglądam trace viewer albo pełny raport z uruchomienia, bo porównanie dobrego i złego przebiegu bywa szybsze niż zgadywanie na podstawie samego stack trace. To szczególnie pomaga w testach UI, gdzie kluczowe jest nie tylko to, co się wywaliło, ale w którym momencie stan aplikacji przestał być zgodny z oczekiwaniem. Kiedy już wiem, z czym mam do czynienia, przechodzę do naprawy bez zgadywania.

Jak naprawiam taki test krok po kroku

Nie zaczynam od podnoszenia timeoutów. To najprostsza, ale zwykle najmniej wartościowa reakcja. Najpierw chcę ustalić, czy test można uczynić przewidywalnym, a dopiero potem decyduję, czy trzeba zmienić samą strategię testowania.

  1. Odtwarzam problem możliwie najprościej - wycinam z testu wszystko, co nie jest potrzebne do reprodukcji. Im mniej ruchomych elementów, tym łatwiej znaleźć źródło niestabilności.
  2. Usuwam losowość - jeśli w teście pojawia się generator danych, zależność od aktualnego czasu albo losowy identyfikator, zamieniam to na wartości kontrolowane.
  3. Izoluję stan - osobna baza, osobne konto, osobny rekord, osobny katalog roboczy. Test powinien wiedzieć tylko tyle, ile naprawdę musi wiedzieć.
  4. Zastępuję czekanie na czas czekaniem na warunek - zamiast sztucznego opóźnienia wolę sprawdzać, czy UI, API albo zadanie w tle faktycznie osiągnęło oczekiwany stan.
  5. Skracam zakres testu - jeśli końcowy scenariusz jest zbyt ciężki, przenoszę część sprawdzeń niżej, do testów integracyjnych lub jednostkowych.
  6. Retry traktuję jako bezpiecznik, nie naprawę - w praktyce 1-2 dodatkowe podejścia mogą pomóc złapać dane diagnostyczne, ale nie powinny być stałym sposobem życia testu.

W Playwright domyślny timeout testu to 30 sekund, a opcja failOnFlakyTests pozwala potraktować wykrytą niestabilność jako błąd na CI, zamiast udawać, że wszystko jest w porządku. To dobre podejście, bo od razu oddziela „chwilowo udało się przejść” od „problem naprawdę rozwiązany”. Po takiej naprawie zostaje jeszcze jeden temat: jak nie dopuścić do powrotu problemu.

Jak nie dopuścić do nawrotu problemu

Najlepsza obrona przed niestabilnymi testami to projektowanie testów tak, by od początku były hermetyczne. Hermetyczny test działa w kontrolowanych warunkach i nie opiera się na przypadkowych zależnościach, które mogą zmienić wynik bez zmiany kodu.

  • Używam stabilnych selektorów - jeśli test UI opiera się na kruchych selektorach CSS albo tekście, który często się zmienia, proszę się o problemy.
  • Nie zostawiam arbitralnych opóźnień - `sleep` maskuje objaw, ale nie wyjaśnia przyczyny.
  • Dbam o dane testowe - każdy test powinien mieć własny, przewidywalny zestaw danych albo sposób ich tworzenia.
  • Kontroluję zależności zewnętrzne - tam, gdzie to ma sens, używam mocków, stubów albo kontraktów zamiast prawdziwych, zmiennych usług.
  • Nie przeciążam CI - wolna maszyna, brak pamięci albo zbyt agresywna paralelizacja potrafią wyglądać jak problem w teście, choć źródło leży w infrastrukturze.
  • Śledzę flake rate - jeśli dany test zaczyna wracać z retry zbyt często, to jest dług techniczny, nie „normalny szum”.

W praktyce najbardziej opłaca się stosować prostą zasadę: najpierw kontrola, potem automatyczne obejścia. Jeśli test wymaga ciągłego poprawiania przez retry, to nie jest jeszcze stabilny. To właśnie w tym miejscu zespół powinien zdecydować, czy wystarczy poprawka, czy trzeba przepisać test głębiej.

Kiedy przepisać test zamiast go łatać

Nie każdy niestabilny test powinien być naprawiany tym samym sposobem. Czasem problem nie leży w detalach implementacji, tylko w samej strategii testowania. I wtedy poprawianie selektorów czy timeoutów daje tylko krótką ulgę.

  • Jeśli winne są zewnętrzne usługi - rozważ mock albo test kontraktowy, zamiast opierać cały scenariusz na podatnym na opóźnienia API.
  • Jeśli test sprawdza zbyt wiele rzeczy naraz - rozbij go na mniejsze, bardziej jednoznaczne przypadki.
  • Jeśli problemem jest UI i animacje - testuj stan końcowy, nie szczegóły przejściowe.
  • Jeśli źródłem jest współdzielony stan - zmień sposób przygotowania danych, a nie tylko kolejność uruchamiania.
  • Jeśli flake wraca mimo kilku poprawek - czasem lepiej przenieść sprawdzenie niżej w piramidzie testów niż dalej walczyć z ciężkim E2E.

Gdybym miał zostawić jedną praktyczną zasadę, byłaby prosta: nie przyzwyczajaj zespołu do testu, który „zwykle działa”. To właśnie takie testy najskuteczniej rozmywają jakość całego procesu. Lepiej mieć mniej testów, ale naprawdę przewidywalnych, niż rozbudowany zestaw, którego nikt już nie ufa i którego każdy po cichu ponawia ręcznie.

FAQ - Najczęstsze pytania

Niestabilny test to taki, który przy tych samych danych wejściowych i bez zmian w kodzie, raz przechodzi, a raz kończy się błędem. Oznacza to brak determinizmu i osłabia zaufanie do automatyzacji testów.
Flaky testy spowalniają proces CI/CD, generują fałszywe alarmy, obniżają zaufanie zespołu do testów automatycznych i mogą maskować prawdziwe regresje w kodzie, prowadząc do ignorowania istotnych problemów.
Najczęstsze przyczyny to: race conditions, współdzielony stan (np. baza danych), zależności zewnętrzne, różnice między środowiskiem lokalnym a CI, zbyt sztywne założenia czasowe oraz brak izolacji testów.
Należy odtworzyć problem, usunąć losowość, izolować stan, zastąpić czekanie na czas czekaniem na warunek, skrócić zakres testu i unikać arbitralnych opóźnień. Retry powinny być bezpiecznikiem, nie rozwiązaniem.
Przepisać test warto, gdy problemem są zewnętrzne usługi (użyć mocków), test sprawdza zbyt wiele rzeczy naraz (rozbić go), winne jest UI i animacje (testować stan końcowy), lub gdy flake wraca mimo wielu poprawek.
Oceń artykuł

Średnia: 0.0 / 5 · 0 ocen

Tagi

flaky test naprawa niestabilnych testów jak radzić sobie z flaky tests przyczyny flaky testów eliminacja niestabilnych testów stabilizacja testów automatycznych
Autor Kazimierz Sadowski
Kazimierz Sadowski
Nazywam się Kazimierz Sadowski i od 4 lat zajmuję się tematyką technologii, sztucznej inteligencji oraz zarządzania projektami. Moja przygoda z tymi dziedzinami zaczęła się z fascynacji możliwościami, jakie niesie ze sobą nowoczesna technologia. Uwielbiam zgłębiać zawirowania AI i analizować, jak wpływa ona na nasze życie oraz sposób pracy w projektach. Pisząc, staram się przedstawiać skomplikowane zagadnienia w przystępny sposób, porównując różne źródła i śledząc aktualne trendy. Zależy mi na tym, aby dostarczać moim czytelnikom rzetelne, zrozumiałe i aktualne informacje, które pomogą im lepiej zrozumieć otaczający nas świat technologii.
Komentarze (0)
Dodaj komentarz