Trwa sprzedaż biletówTesting Ground Conference, bilety 30% taniej

Organizujemy Testing Ground Conference, jedną z największych konferencji QA w Polsce. Kod poniżej daje 30% na każdy bilet.

Kup bilety
Pętle w Javie: for, while, do while i for-each z przykładami dla testerów

Czas czytania: około 16 minut

Test automatyczny, który sprawdza jeden produkt w koszyku, jest demem, a nie testem. Sklep ma tysiące produktów, formularz ma dziesiątki kombinacji danych, a odpowiedź API zwraca listę, nie pojedynczy rekord. Moment, w którym skrypt przestaje sprawdzać jeden przypadek i zaczyna przechodzić przez wszystkie, to moment, w którym w kodzie pojawia się pętla. I to właśnie pętle, a nie asercje, odróżniają zestaw testów od zestawu przykładów.

W tym artykule przechodzimy przez wszystkie rodzaje pętli w Javie: for, while, do while i for-each, z działającymi przykładami, tabelą porównawczą, tym samym kodem w Pythonie i JavaScript oraz listą błędów, które najczęściej wywracają testy. Materiał jest częścią kursu programowania w Javie dla testerów, po lekcji o instrukcji warunkowej if.

Pętle w trzech zdaniach

Pętla to konstrukcja, która wykonuje ten sam blok kodu wiele razy, dopóki spełniony jest warunek albo dopóki nie skończą się elementy do przejrzenia. Java ma cztery rodzaje pętli: for (znana liczba powtórzeń), while (powtarzaj, dopóki warunek jest prawdziwy), do while (jak while, ale ciało wykona się co najmniej raz) i for-each (po każdym elemencie tablicy lub kolekcji). W testach automatycznych pętle przechodzą po danych testowych, po elementach interfejsu i po rekordach z odpowiedzi API, a najczęstsze błędy to pętla nieskończona, pomyłka o jeden i zmiana kolekcji w trakcie jej przeglądania.

Co to jest pętla w programowaniu?

Pętla to instrukcja, która każe programowi wykonać ten sam fragment kodu wielokrotnie, zamiast przepisywać go kilkanaście razy. Ma dwie części: ciało, czyli blok instrukcji do powtórzenia, oraz warunek, który decyduje, czy wykonać kolejne przejście. Każde przejście przez ciało to iteracja. Gdy warunek przestaje być prawdziwy albo kończą się elementy do przetworzenia, program wychodzi z pętli i wykonuje kod znajdujący się za nią.

Według specyfikacji języka Java SE 21 rozdział 14 opisuje pętle jako instrukcje iteracyjne i wyróżnia 4 formy: while, do, for w wersji podstawowej oraz for w wersji rozszerzonej, którą potocznie nazywa się for-each. Wszystkie cztery robią to samo, czyli powtarzają blok kodu, i różnią się tylko tym, gdzie stoi warunek, kto pilnuje licznika i czy pętla wie z góry, ile razy ma się wykonać.

Dla testera pętla jest narzędziem numer jeden przy pracy z danymi. Zestaw danych testowych to lista, wynik wyszukiwania na stronie to lista, odpowiedź API zwykle zawiera listę. Bez pętli test sprawdza pierwszy element i udaje, że reszta wygląda tak samo. Jak przełożyć podstawy Javy na realną automatyzację, opisujemy w artykule o tym, jak zacząć naukę Javy jako tester automatyzujący.

Rodzaje pętli w Javie w jednej tabeli

Cztery pętle, jedno pytanie: skąd pętla wie, kiedy przestać. Odpowiedź na to pytanie wybiera za Was właściwą konstrukcję, i to jest jedyna reguła, którą trzeba zapamiętać.

Pętla Kiedy sprawdza warunek Minimalna liczba wykonań Używajcie, gdy Typowe zastosowanie w testach
for przed każdym przejściem 0 liczba powtórzeń jest znana z góry albo potrzebny jest indeks powtórzenie kroku N razy, dostęp do elementu po numerze
while przed każdym przejściem 0 powtarzacie, dopóki coś jest prawdą, bez znanej liczby przejść czekanie na stan strony, przechodzenie po stronach wyników
do while po każdym przejściu 1 ciało musi wykonać się co najmniej raz, zanim będzie co sprawdzać ponowienie żądania, menu wyboru, pobranie pierwszej strony
for-each nie ma warunku, idzie po elementach 0 przeglądacie każdy element tablicy albo kolekcji i nie potrzebujecie indeksu asercja na każdym wierszu tabeli, każdym rekordzie z API

Z tabeli wynika prosta zasada praktyczna. Jeśli macie kolekcję i chcecie zrobić coś z każdym elementem, piszecie for-each. Jeśli liczycie do N albo potrzebujecie numeru elementu, piszecie for. Jeśli czekacie, aż coś się stanie, piszecie while. Do while zostaje na przypadki, gdy pierwsze wykonanie jest obowiązkowe. W praktyce Quality Island for-each i for pokrywają ponad 90 procent pętli w kodzie testów, a do while pojawia się rzadko i zwykle przy ponawianiu żądań.

Pętla for: gdy wiecie, ile razy

Pętla for ma w nagłówku wszystko, co potrzebne, żeby ją zrozumieć bez czytania ciała: skąd zaczyna, do kiedy idzie i o ile się przesuwa. Te trzy części to inicjalizacja, warunek i krok, rozdzielone średnikami. Inicjalizacja wykonuje się raz, na starcie. Warunek jest sprawdzany przed każdym przejściem, także pierwszym, więc pętla for może nie wykonać się ani razu. Krok wykonuje się po każdym przejściu ciała.

for (int i = 1; i <= 5; i++) {
    System.out.println("Próba numer " + i);
}
// wypisze: Próba numer 1, Próba numer 2, ... Próba numer 5

Zmienna sterująca, tu i, żyje tylko wewnątrz pętli. Po wyjściu z niej Java nie pozwoli jej użyć, i to jest celowe: licznik nie powinien wyciekać do reszty kodu. Krok nie musi być jedynką. Zapis i += 2 przejdzie po co drugim elemencie, a i-- odliczy w dół. Warunek i <= 5 daje pięć przejść, warunek i < 5 daje cztery, i to jest pierwsze miejsce, gdzie w testach pojawiają się pomyłki o jeden.

String[] loginy = {"anna", "bartek", "celina"};
for (int i = 0; i < loginy.length; i++) {
    System.out.println("Element " + i + ": " + loginy[i]);
}
// tablice w Javie numerowane są od zera, ostatni indeks to length - 1

Pętla for z indeksem jest właściwym wyborem wtedy, gdy numer elementu ma znaczenie: sprawdzacie, czy trzeci wiersz tabeli zawiera sumę, porównujecie element z poprzednim albo przechodzicie po dwóch tablicach naraz. Jeśli indeks nie jest do niczego potrzebny, lepszym wyborem jest for-each, o którym niżej.

Pętla while: gdy liczy się warunek, a nie licznik

Pętla while powtarza ciało tak długo, jak długo warunek w nawiasie jest prawdziwy, i nie interesuje jej, ile razy to będzie. Sprawdza warunek przed każdym przejściem, więc jeśli warunek jest fałszywy od początku, ciało nie wykona się wcale. Cała odpowiedzialność za to, żeby warunek kiedyś przestał być prawdziwy, leży po stronie kodu w ciele pętli.

int proba = 0;
while (!stronaZaladowana() && proba < 10) {
    czekaj(500);
    proba++;
}
// czeka maksymalnie 10 razy po pół sekundy, potem idzie dalej

Ten przykład pokazuje wzorzec, który w testach interfejsu pojawia się nieustannie: powtarzaj sprawdzenie, dopóki strona nie osiągnie oczekiwanego stanu, ale nie dłużej niż określony limit. Drugi warunek, proba < 10, jest zabezpieczeniem przed pętlą nieskończoną. Bez niego test zawieszony na stronie, która nigdy się nie załaduje, będzie czekał do końca świata albo do zabicia procesu przez CI. Jak to samo robią gotowe mechanizmy oczekiwania, opisujemy w artykule o waitach w Selenium WebDriver.

While jest też naturalnym wyborem przy przechodzeniu po stronach wyników, gdy nie wiecie, ile ich będzie: pobierzcie stronę, przetwórzcie, sprawdźcie, czy jest następna, powtórzcie. Licznik nie ma tu sensu, bo liczba stron zależy od danych, nie od kodu.

Pętla do while: najpierw wykonaj, potem sprawdź

Do while różni się od while jednym szczegółem: warunek stoi na końcu, więc ciało wykona się co najmniej raz, zanim ktokolwiek go sprawdzi. Składnia ma jeszcze jedną pułapkę: po nawiasie z warunkiem musi stać średnik, o którym początkujący zapominają, a kompilator upomina się o niego niezbyt czytelnym komunikatem.

int kod;
int proba = 0;
do {
    kod = wyslijZadanie();
    proba++;
} while (kod == 503 && proba < 3);
// pierwsze żądanie idzie zawsze, ponowienie tylko przy 503, maksymalnie trzy próby

Ponowienie żądania to najczystszy przypadek użycia do while: pierwsza próba musi pójść, bo bez niej nie ma kodu odpowiedzi do sprawdzenia. Ten sam wzorzec obsługuje menu z wyborem opcji, gdzie program najpierw pokazuje menu, a potem sprawdza, czy użytkownik wybrał wyjście. Poza tymi sytuacjami do while spotyka się rzadko i wielu programistów pisze zamiast niego while z ręcznie wykonanym pierwszym krokiem, co jest dłuższe, ale dla niektórych czytelniejsze.

Pętla for-each: po każdym elemencie kolekcji

For-each, w specyfikacji Javy nazywana rozszerzoną instrukcją for, przechodzi po każdym elemencie tablicy albo kolekcji bez licznika, bez indeksu i bez możliwości pomyłki o jeden. Zmienna w nagłówku przyjmuje kolejno wartość każdego elementu, a pętla kończy się sama, gdy elementy się skończą. To najczęstsza pętla w kodzie testów i zwykle najbezpieczniejsza.

List<WebElement> wiersze = driver.findElements(By.cssSelector("table.zamowienia tr"));
for (WebElement wiersz : wiersze) {
    String status = wiersz.findElement(By.className("status")).getText();
    Assertions.assertNotEquals("", status, "Wiersz bez statusu: " + wiersz.getText());
}
// ta sama asercja dla każdego wiersza, niezależnie od tego, ile ich jest

For-each działa na tablicach i na wszystkim, co implementuje interfejs Iterable, czyli na listach, zbiorach i większości kolekcji z biblioteki standardowej. Nie działa bezpośrednio na mapie, ale działa na jej widokach: mapa.keySet(), mapa.values() i mapa.entrySet(). Ma dwa ograniczenia, o których trzeba pamiętać. Nie daje dostępu do indeksu, więc gdy potrzebujecie numeru elementu, wracacie do zwykłego for. I nie pozwala bezpiecznie usuwać ani dodawać elementów w trakcie przeglądania, o czym więcej w sekcji o błędach. Jak zamienić sprawdzenie w pętli w czytelną asercję, pokazujemy w artykule o asercjach w testach automatycznych.

break i continue: jak sterować pętlą od środka

Dwie instrukcje pozwalają zmienić bieg pętli z wnętrza jej ciała: break przerywa pętlę natychmiast, continue pomija resztę bieżącego przejścia i skacze do następnego. Obie działają we wszystkich czterech rodzajach pętli.

for (String login : loginy) {
    if (login.isBlank()) {
        continue;            // pusty login pomijamy, idziemy do następnego
    }
    if (login.equals("admin")) {
        break;               // znaleźliśmy admina, dalsze szukanie nie ma sensu
    }
    sprawdzLogowanie(login);
}

Break przydaje się przy szukaniu: gdy znajdziecie to, czego szukaliście, dalsze przejścia to strata czasu. Continue przydaje się przy filtrowaniu: elementy, które nie spełniają warunku, pomijacie bez zagnieżdżania reszty ciała w wielkim if. Java ma też wersję z etykietą, która przerywa pętlę zewnętrzną z wnętrza wewnętrznej, ale jeśli jej potrzebujecie, to zwykle sygnał, że zagnieżdżone pętle warto wyciągnąć do osobnej metody. Jak dzielić kod na metody, opisujemy w lekcji o funkcjach w Javie.

Ta sama pętla w Javie, Pythonie i JavaScript

Pętle wyglądają podobnie w każdym języku, którego używa się do automatyzacji, i ta tabela pozwala przełożyć nawyki z jednego na drugi. Największa różnica dotyczy Pythona, który nie ma pętli do while ani klasycznej pętli for z licznikiem: zamiast niej używa for po zakresie generowanym przez range().

Zadanie Java Python JavaScript
Pięć powtórzeń for (int i = 0; i < 5; i++) for i in range(5): for (let i = 0; i < 5; i++)
Po każdym elemencie for (String x : lista) for x in lista: for (const x of lista)
Dopóki warunek while (warunek) while warunek: while (warunek)
Co najmniej raz do { } while (warunek); brak, while z break do { } while (warunek);
Element z indeksem zwykły for i lista.get(i) for i, x in enumerate(lista): lista.forEach((x, i) => ...)
Przerwanie i pominięcie break, continue break, continue break, continue

Wybór języka do automatyzacji to osobna decyzja, którą omawiamy w artykule jaki język programowania wybrać do automatyzacji testów. Z punktu widzenia pętli ta decyzja nie ma większego znaczenia: kto rozumie for, while i for-each w jednym języku, przeczyta je w każdym innym w kwadrans.

Pętle w testach automatycznych: trzy zastosowania

W kodzie testów pętle pojawiają się w trzech miejscach, i każde z nich ma swój rodzaj pętli oraz swoją pułapkę.

Dane testowe

Jeden test, wiele zestawów danych: poprawne i niepoprawne loginy, kwoty graniczne, formaty dat. Pętla for-each po liście zestawów uruchamia to samo sprawdzenie dla każdego.

Pułapka: gdy test w pętli pada na trzecim zestawie, komunikat musi mówić, na którym. Bez tego szukacie igły w stogu siana. W JUnit 5 lepiej użyć testu parametryzowanego, który robi to za Was.

Elementy interfejsu

Wszystkie wiersze tabeli, wszystkie linki w menu, wszystkie produkty na liście. For-each po wyniku findElements i asercja w każdym przejściu.

Pułapka: strona przeładowała się w trakcie pętli i elementy z listy przestały istnieć. Selenium zgłosi StaleElementReferenceException. Listę trzeba pobrać ponownie albo przejść po indeksach.

Odpowiedzi API

Lista zamówień w odpowiedzi JSON, każde ma mieć identyfikator, datę i status z dozwolonego zbioru. For-each po rekordach, a while po stronach wyników, dopóki API zwraca następną.

Pułapka: pusta lista przechodzi test, bo pętla nie wykonała się ani razu i żadna asercja nie padła. Przed pętlą sprawdźcie, że lista nie jest pusta.

Ostatnia pułapka jest zdradliwa i wraca w audytach zestawów testów regularnie. Pętla po pustej kolekcji jest poprawna składniowo, wykonuje się zero razy i test kończy się sukcesem, mimo że nie sprawdził niczego. W projekcie dla Argos, sklepu internetowego, przeniesienie sprawdzeń z klikania w interfejs na pętle po danych z API było jednym z elementów, dzięki którym regresja skróciła się z 5 dni do 10 godzin, a liczba błędów krytycznych na produkcji spadła o 46 procent według danych klienta. Co opłaca się automatyzować w ten sposób, a co nie, liczymy w artykule na Strefie QA.

450 000+
testów automatycznych napisanych przez zespół Quality Island, większość z nich przechodzi w pętli po danych albo elementach
4
rodzaje pętli w Javie według specyfikacji języka: for, while, do while i for-each
0
tyle razy wykona się pętla for-each po pustej liście, a test i tak zakończy się sukcesem
5 dni na 10 h
skrócenie regresji u naszego klienta z e-commerce po przeniesieniu sprawdzeń na poziom danych

Najczęstsze błędy w pętlach i jak je wyłapać

Błędy w pętlach są tanie do zrobienia i drogie do znalezienia, bo kod wygląda poprawnie i kompiluje się bez ostrzeżeń. Poniższa tabela zbiera te, które w kodzie testów widzimy najczęściej.

Błąd Jak wygląda Skutek Jak go uniknąć
Pętla nieskończona warunek while nigdy nie staje się fałszywy, bo w ciele nikt nie zmienia zmiennej test wisi do przekroczenia limitu czasu w CI drugi warunek z licznikiem prób albo limitem czasu
Pomyłka o jeden i <= length zamiast i < length ArrayIndexOutOfBoundsException na ostatnim przejściu for-each tam, gdzie indeks nie jest potrzebny
Zmiana kolekcji w for-each usunięcie elementu z listy w trakcie przeglądania jej for-each ConcurrentModificationException Iterator z metodą remove albo removeIf
Pusta kolekcja pętla z asercjami po liście, która okazała się pusta test przechodzi, choć niczego nie sprawdził asercja na rozmiar listy przed pętlą
Średnik po nagłówku for (...); z średnikiem przed klamrą ciało wykonuje się raz, po pętli, a pętla kręci się na pusto formatowanie kodu i przegląd, kompilator tego nie zgłosi
Nieaktualne elementy strony for-each po elementach pobranych przed przeładowaniem strony StaleElementReferenceException w losowym przejściu pobranie listy ponownie w każdym przejściu albo pętla po indeksach

Dwa z tych błędów zasługują na osobne zdanie. Średnik po nagłówku pętli jest w pełni poprawną Javą, bo pusty średnik to pusta instrukcja, więc kompilator nie ma się do czego przyczepić. Z naszego doświadczenia to jeden z najtrudniejszych do zauważenia błędów w przeglądzie kodu, bo oko czyta nagłówek i klamrę jako całość. A pusta kolekcja to błąd, który nie rzuca żadnego wyjątku i przez to potrafi żyć w zestawie testów miesiącami, dając zielony wynik za sprawdzenie zera elementów.

Kiedy nie używać pętli

Od Javy 8 wiele pętli da się zastąpić strumieniami, a w testach parametryzowanych pętlę po danych zastępuje framework. Zapis lista.stream().filter(...).map(...).collect(...) robi to samo co pętla z if w środku, tylko deklaruje, co ma powstać, zamiast opisywać krok po kroku, jak to zrobić. Dla prostych operacji, filtrowania i przekształcania, strumień jest zwykle czytelniejszy. Dla logiki z wieloma warunkami, licznikami i wczesnym wyjściem pętla wygrywa, bo strumień w takim przypadku staje się nieczytelny.

W testach najważniejszy przypadek to dane testowe. Pętla for-each po liście zestawów działa, ale gdy pada, raportuje jeden nieudany test i ukrywa, który zestaw zawinił. Test parametryzowany w JUnit 5, oznaczony adnotacją @ParameterizedTest, uruchamia osobny przypadek dla każdego zestawu i raportuje każdy osobno. To ta sama pętla, tylko wykonana przez framework, z lepszym raportem. Jeśli piszecie pętlę po danych testowych z asercją w środku, to niemal zawsze sygnał, że powinien to być test parametryzowany. W lekcji o klasach i obiektach pokazujemy, jak spakować zestaw danych w obiekt, który taki test przyjmuje jako parametr.

Najczęstsze pytania o pętle

Co to jest pętla?

Konstrukcja, która wykonuje ten sam blok kodu wiele razy, dopóki warunek jest prawdziwy albo dopóki nie skończą się elementy do przetworzenia. Każde wykonanie bloku to iteracja.

Jakie są rodzaje pętli w Javie?

Cztery: for z licznikiem, while, do while i for-each po elementach kolekcji. Różnią się tym, gdzie stoi warunek i czy pętla zna z góry liczbę przejść.

Czym różni się while od do while?

While sprawdza warunek przed każdym przejściem, więc może nie wykonać się wcale. Do while sprawdza warunek po przejściu, więc wykona się co najmniej raz.

Kiedy używać for, a kiedy for-each?

For-each zawsze wtedy, gdy przeglądacie każdy element kolekcji i nie potrzebujecie jego numeru. Zwykły for wtedy, gdy liczycie do N, potrzebujecie indeksu albo przechodzicie po dwóch kolekcjach naraz.

Czy pętla for-each działa na mapie?

Nie bezpośrednio, bo mapa nie jest kolekcją. Działa na jej widokach: keySet() po kluczach, values() po wartościach i entrySet() po parach klucz i wartość.

Jak przerwać pętlę przed końcem?

Instrukcją break, która wychodzi z pętli natychmiast. Continue pomija tylko bieżące przejście i idzie do następnego.

Co zabrać z tego artykułu

01Jedna reguła wyboru: kolekcja bez indeksu to for-each, liczenie do N to for, czekanie na warunek to while, obowiązkowe pierwsze wykonanie to do while.

02Każda pętla while w teście potrzebuje drugiego warunku z limitem prób albo czasu. Bez niego test zawieszony na stronie czeka do zabicia przez CI.

03Pętla po pustej liście wykonuje się zero razy i test przechodzi, choć nic nie sprawdził. Przed pętlą z asercjami sprawdźcie rozmiar listy.

04Nie usuwajcie elementów z kolekcji wewnątrz for-each. Java zgłosi ConcurrentModificationException, czasem dopiero na produkcji danych.

05Pętla po danych testowych z asercją w środku to sygnał, że powinien to być test parametryzowany. Ten sam kod, lepszy raport.

Chcecie przejść od pętli i instrukcji warunkowych do pierwszego działającego testu automatycznego w Javie? Na szkoleniu piszemy go od zera, na realnej aplikacji, z trenerem, który na co dzień pracuje w projektach QA.

Zobaczcie program szkolenia


Powiązane na blogu Quality Island

Pełna lista źródeł

Co o tym sądzisz?

Dodaj komentarz

Bądź na bieżąco
Bądź na bieżąco
Automatyzacja testów z narzędziem Playwright (Python)
Automatyzacja testów z narzędziem Playwright (C#)

Pierwotna cena wynosiła: 2657,00 PLN.Aktualna cena wynosi: 2399,00 PLN.Ta cena wzrośnie za 5 dni!

12.10.26, 02.11.26, 23.11.26, 14.12.26, 18.01.27
2 dni
Automatyzacja testów z narzędziem Playwright (Python)
Automatyzacja testów z narzędziem Playwright (Python)

Pierwotna cena wynosiła: 2670,00 PLN.Aktualna cena wynosi: 2399,00 PLN.Ta cena wzrośnie za 1 dzień!

08.10.26, 27.10.26, 17.11.26, 09.12.26, 14.01.27
2 dni
Automatyzacja testów z narzędziem Playwright
Automatyzacja testów z narzędziem Playwright (Java)

2661,00 PLN

15.10.26, 05.11.26, 26.11.26, 17.12.26, 20.01.27
2 dni
Popularne artykuły
Język Gherkin: co to jest i jak go używać w testowaniu oprogramowania
Smoke test: co to jest, kiedy go uruchamiać i czym różni się od sanity testu
Jak zostać testerem oprogramowania: ścieżka krok po kroku bez dyplomu informatyka
Najnowsze artykuły
Audyt QA w sektorze regulowanym: co pokazuje case PKO BP
Jak skrócić testy regresyjne z 5 dni do 10 godzin: case Argos
Cyber Resilience Act: nowy obowiązek testowania podatności przez cały cykl życia produktu
Popularne kategorie