Flaky testy Selenium? Opanuj waits i synchronizację!

Lista kontrolna stabilizacji niestabilnych testów Selenium: zastąp `Thread.sleep` przez `selenium wait` (jawne oczekiwanie), generuj unikalne dane, unikaj ponownych prób przy operacjach nieodwracalnych.

Napisano przez

Dawid Kowalczyk

Opublikowano

21 mar 2026

Spis treści

W testach automatycznych najwięcej problemów zwykle nie robi sam selektor, tylko moment wykonania akcji. Gdy aplikacja ładuje dane asynchronicznie, animuje komponenty albo zmienia DOM po kliknięciu, test potrafi działać raz dobrze, a raz rzucać wyjątek bez zmiany kodu. W praktyce termin selenium wait najlepiej rozumieć jako zestaw sposobów na zsynchronizowanie testu z tym, co faktycznie dzieje się w przeglądarce.

Najważniejsze zasady stabilnego oczekiwania w Selenium

  • Nie opieraj się na stałym śnie, bo to najprostsza droga do niestabilnych testów i niepotrzebnie długich przebiegów.
  • Implicit wait działa globalnie i pomaga przy wyszukiwaniu elementów, ale nie rozwiązuje problemu widoczności ani klikalności.
  • Jawne oczekiwania zwykle dają najlepszą kontrolę, bo opisują konkretny stan aplikacji, a nie sam upływ czasu.
  • Warunek powinien pasować do kroku testu: inny dla widoczności, inny dla znikania spinnera, inny dla odświeżenia elementu.
  • Własny wait ma sens, gdy gotowe warunki nie opisują biznesowego stanu, na którym naprawdę Ci zależy.
  • Standaryzacja w zespole ważniejsza jest niż pojedyncza „sprytna” sztuczka w jednym teście.

Skąd biorą się błędy synchronizacji w testach Selenium

Najczęściej problem zaczyna się od race condition, czyli sytuacji, w której test i aplikacja „śpieszą się” do tego samego kroku, ale nie kończą w tej samej kolejności. Przeglądarka może już przejść na nowy ekran, a komponent JavaScript jeszcze nie dokończył renderowania. Z perspektywy użytkownika trwa to ułamek sekundy, ale dla testu to różnica między sukcesem a wyjątkiem.

To szczególnie widać w aplikacjach SPA, gdzie elementy pojawiają się dopiero po kliknięciu, zmieniają widoczność po animacji albo są podmieniane w DOM bez pełnego przeładowania strony. Sama nawigacja też nie oznacza jeszcze, że wszystko jest gotowe do interakcji. Selenium czeka na określone readyState zgodnie z ustawioną strategią ładowania strony, ale to nie gwarantuje, że dynamiczne skrypty już skończyły pracę.

Właśnie dlatego test, który bez problemu otwiera stronę, potrafi polec przy próbie kliknięcia przycisku, wpisania tekstu albo odczytu wartości z pola. Jeśli po stronie aplikacji jest opóźnienie, overlay, animacja lub niedokończony render, sam fakt „bycia na stronie” niczego jeszcze nie załatwia. I od tego miejsca warto przejść do tego, co w Selenium realnie pomaga.

Implicit wait ma zastosowanie, ale nie rozwiązuje wszystkiego

Implicit wait to globalne ustawienie, które mówi driverowi, jak długo ma próbować znaleźć element, zanim zgłosi błąd. Domyślnie wynosi 0, więc bez dodatkowej konfiguracji Selenium od razu zwróci wyjątek, jeśli elementu jeszcze nie ma w DOM. To bywa wygodne, ale jego zakres jest wąski: pomaga przy samym wyszukiwaniu, nie przy interakcji z gotowym, lecz jeszcze niewidocznym elementem.

W praktyce traktuję to raczej jako prosty bufor niż główną strategię synchronizacji. Działa na wszystkie wywołania lokalizujące elementy w danej sesji, więc łatwo nim niechcący spowolnić cały zestaw testów. Co ważne, po znalezieniu elementu driver nie czeka dalej, więc większa wartość nie musi automatycznie wydłużać czasu całego przebiegu.

Metoda Co robi Największa zaleta Ograniczenie
Stały sleep Wstrzymuje test na z góry ustalony czas Prosty do napisania Albo za krótki, albo niepotrzebnie spowalnia test
Implicit wait Czeka na znalezienie elementu podczas lokalizacji Minimalna konfiguracja globalna Nie rozwiązuje problemu widoczności, klikalności ani animacji
Explicit wait Czeka na konkretny warunek stanu aplikacji Najlepsza kontrola i czytelność intencji Wymaga dobrania właściwego warunku do sytuacji

Jeśli miałbym zostawić tylko jedną zasadę, brzmiałaby tak: nie mieszaj implicit i explicit waitów bez bardzo dobrego powodu. Selenium samo ostrzega, że takie połączenie potrafi dać nieprzewidywalne czasy oczekiwania. W dokumentacji projekt podaje nawet przykład, w którym 10 sekund implicit i 15 sekund explicit może skończyć się timeoutem po około 20 sekundach, a nie po intuicyjnych 15.

driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(2));

W prostych testach to bywa wystarczające jako lekka poduszka bezpieczeństwa, ale przy dynamicznych interfejsach zwykle potrzebujesz czegoś precyzyjniejszego. I właśnie tu jawne oczekiwania zaczynają wygrywać bez dyskusji.

Dlaczego jawne oczekiwania zwykle dają lepszą kontrolę

Jawne oczekiwanie mówi testowi nie „poczekaj chwilę”, tylko „poczekaj, aż wydarzy się dokładnie to”. W Selenium działa to przez pętlę, która sprawdza warunek w odstępach czasu i kończy się dopiero wtedy, gdy warunek stanie się prawdziwy albo minie limit czasu. Domyślny interwał odpytywania w Pythonie to 0,5 sekundy, więc test nie blokuje się bez końca, tylko regularnie sprawdza stan aplikacji.

To podejście dobrze skaluje się w nowoczesnych aplikacjach, bo warunek może dotyczyć nie tylko obecności elementu, ale też jego widoczności, klikalności, zniknięcia, zmiany tekstu czy odświeżenia. Selenium dostarcza do tego gotowe zestawy warunków, a w praktyce najczęściej używa się tych, które opisują realną intencję użytkownika: „przycisk jest klikalny”, „spinner zniknął”, „tekst się pojawił”, „stary element został podmieniony”.

Przykład w Javie wygląda prosto i czytelnie:

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#save"))).click();

To jest właśnie ten moment, w którym explicit wait daje przewagę nad prostym opóźnieniem: test nie marnuje czasu, jeśli aplikacja jest gotowa szybciej, ale też nie rusza dalej za wcześnie. Dobrze dobrany warunek jest zwykle ważniejszy niż sam czas oczekiwania. I to prowadzi do pytania, który warunek wybrać w konkretnym scenariuszu.

Jak dobierać warunek do konkretnego kroku testu

Najczęstszy błąd początkujących polega na tym, że czekają na cokolwiek, zamiast czekać na ten jeden stan, który naprawdę otwiera drogę do następnej akcji. Ja zwykle zaczynam od pytania: co musi być prawdą, żeby użytkownik mógł bezpiecznie zrobić kolejny krok? Odpowiedź na to pytanie prowadzi do właściwego warunku.

Sytuacja Lepszy warunek Dlaczego to działa
Element pojawia się dopiero po kliknięciu Widoczność elementu Sam fakt obecności w DOM nie wystarczy, jeśli komponent jest jeszcze ukryty
Przycisk jest na stronie, ale przykrywa go animacja lub overlay Klikalność elementu Test czeka, aż element będzie nie tylko widoczny, ale też możliwy do użycia
Treść ma się zaktualizować po akcji Obecność konkretnego tekstu Sprawdzasz efekt biznesowy, a nie tylko techniczny stan strony
Spinner lub loader ma zniknąć Zniknięcie elementu To zwykle najlepszy sygnał, że backend i UI skończyły pracę
Stary element został podmieniony po rerenderze Staleness elementu Pomaga uniknąć pracy na referencji, która już nie odpowiada aktualnemu DOM

Ta tabela wygląda prosto, ale w praktyce oszczędza mnóstwo czasu. Zamiast zgadywać, czy trzeba czekać 2, 5 czy 10 sekund, opisujesz stan, który ma wystąpić. I właśnie taki sposób myślenia odróżnia poprawny test od testu, który tylko czasem przechodzi.

Kiedy napisać własny wait zamiast polegać na gotowych warunkach

Są sytuacje, w których gotowe warunki nie wystarczą, bo biznesowy stan aplikacji jest zbyt specyficzny. Może to być licznik rekordów po filtrowaniu, toast z konkretnym komunikatem, zakończenie renderowania wykresu, podmiana atrybutu data-status albo ustąpienie błędu po stronie frontendu. Wtedy własny wait jest rozsądniejszy niż wciskanie na siłę jednego z generycznych warunków.

W praktyce opiera się to zwykle o FluentWait albo o własny warunek przekazany do until. Tu możesz kontrolować częstotliwość odpytywania, listę ignorowanych wyjątków i samą logikę sprawdzania. To szczególnie przydatne, gdy aplikacja reaguje wolniej niż przeciętna strona albo gdy element przez chwilę znika i wraca w nowej postaci.

new WebDriverWait(driver, Duration.ofSeconds(8))
    .pollingEvery(Duration.ofMillis(250))
    .ignoring(NoSuchElementException.class)
    .until(d -> d.findElement(By.id("status")).getText().contains("Gotowe"));

Nie warto jednak ignorować zbyt wielu wyjątków „na zapas”. Jeśli zbyt szeroko wyciszysz błędy, test przestanie informować o realnym problemie i zacznie udawać stabilność. Sensowna zasada jest prosta: ignoruj tylko te wyjątki, które są naturalne w trakcie oczekiwania, a nie wszystkie, które da się złapać.

Najczęstsze błędy, które robią testy flaky

W stabilności testów nie chodzi o pojedynczy trik, tylko o unikanie kilku powtarzalnych wpadek. Najwięcej szkód robią te same schematy, które na początku wydają się „szybkim naprawieniem problemu”, a po tygodniu zamieniają suite w loterię.

  • Stały sleep zamiast warunku - test czeka zawsze tyle samo, nawet jeśli aplikacja jest gotowa znacznie szybciej albo potrzebuje więcej czasu.
  • Mieszanie implicit i explicit waitów - trudniej przewidzieć realny czas timeoutu i trudniej diagnozować przyczyny błędów.
  • Sprawdzanie samej obecności elementu - element może istnieć w DOM, ale nadal być ukryty, zasłonięty lub nieaktywny.
  • Trzymanie starej referencji do WebElement - po rerenderze element staje się nieaktualny i pojawia się problem ze stale reference.
  • Zbyt długie timeouty wszędzie - testy zaczynają maskować regresje zamiast je wykrywać.
  • Ignorowanie animacji i overlayów - kliknięcie trafia w element, który wygląda na gotowy, ale jeszcze nim nie jest.

Najbardziej zdradliwy błąd to ten ostatni, bo wizualnie wszystko wygląda poprawnie, a Selenium nadal nie może wykonać akcji. Jeśli aplikacja używa przejść, wysuwanych paneli albo loaderów, dobrze dobrany warunek ma większe znaczenie niż sam locator. I to prowadzi do ostatniego kroku: ustalenia zasad, które zostają w projekcie na stałe.

Jak zamienić oczekiwanie w stały standard zespołu

Najlepsze wdrożenia nie opierają się na pamięci jednego testera, tylko na prostych regułach, które zespół stosuje wszędzie tak samo. Ja zwykle polecam trzy rzeczy: wspólny zestaw helperów do czekania, jasny standard timeoutów i świadome traktowanie warunków jako opisu zachowania aplikacji, a nie technicznego obejścia problemu.

  • Ustal jeden domyślny timeout dla większości kroków, na przykład 5-10 sekund, a dłuższe wartości zostaw tylko dla naprawdę ciężkich procesów.
  • Trzymaj implicit wait blisko zera, jeśli korzystasz z explicit waitów jako głównej strategii.
  • Dodaj helpery typu `waitForVisible`, `waitForClickable`, `waitForGone`, żeby testy były czytelne i spójne.
  • Czekaj na stan użytkownika, nie na „techniczny spokój” strony. To zwykle lepiej opisuje faktyczny flow.
  • Loguj kontekst timeoutu, czyli locator, aktualny URL i nazwę kroku, bo bez tego diagnoza błędu trwa niepotrzebnie długo.

Jeśli miałbym zostawić jedną praktyczną zasadę na koniec, powiedziałbym tak: dobry wait nie opóźnia testu, tylko precyzyjnie potwierdza gotowość aplikacji. Kiedy oczekiwanie jest zapisane jasno i konsekwentnie, testy są krótsze, stabilniejsze i dużo łatwiejsze do utrzymania, a to w automatyzacji zwykle robi większą różnicę niż kolejny „sprytny” workaround.

FAQ - Najczęstsze pytania

Selenium wait to zestaw technik synchronizacji testu z dynamicznym zachowaniem aplikacji. Jest kluczowe, by uniknąć niestabilnych testów (flaky tests) wynikających z asynchronicznego ładowania danych, animacji czy zmian w DOM. Zapewnia stabilność i wiarygodność automatyzacji.

Implicit wait to globalne oczekiwanie na znalezienie elementu w DOM. Explicit wait to precyzyjne oczekiwanie na spełnienie konkretnego warunku (np. widoczność, klikalność elementu). Daje lepszą kontrolę i jest zalecany dla dynamicznych aplikacji.

Mieszanie tych dwóch typów oczekiwań może prowadzić do nieprzewidywalnych czasów timeoutu i utrudniać diagnozę błędów. Selenium samo ostrzega przed taką praktyką, zalecając stosowanie jednej spójnej strategii dla lepszej stabilności testów.

Częste błędy to: stały sleep, mieszanie implicit i explicit waits, sprawdzanie tylko obecności elementu (bez widoczności/klikalności), używanie starych referencji do WebElement oraz ignorowanie animacji i overlayów, które blokują interakcję.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi:

selenium wait jak stabilizować testy selenium explicit wait selenium najlepsze praktyki implicit wait czy explicit wait selenium

Udostępnij artykuł

Dawid Kowalczyk

Dawid Kowalczyk

Jestem Dawid Kowalczyk, analitykiem branżowym z wieloletnim doświadczeniem w obszarze technologii. Od ponad pięciu lat zajmuję się analizowaniem trendów rynkowych oraz innowacji technologicznych, co pozwoliło mi zgromadzić głęboką wiedzę na temat najnowszych osiągnięć w tej dziedzinie. Moim celem jest uproszczenie złożonych danych oraz dostarczanie obiektywnej analizy, która pomoże czytelnikom lepiej zrozumieć dynamiczny świat technologii. Wierzę w siłę rzetelnych informacji, dlatego dokładam wszelkich starań, aby moje teksty były aktualne i oparte na wiarygodnych źródłach. Jako doświadczony twórca treści, dążę do tego, aby każdy artykuł dostarczał wartościowych informacji, które są nie tylko interesujące, ale także użyteczne dla moich czytelników.

Napisz komentarz