Debugowanie padających testów Playwright jak pro
Nieudane testy to nie problemy — to wskazówki. Dowiedz się, jak używać trybu UI Playwright, przeglądarki trace, debugowania headed i analizy artefaktów, aby izolować niestabilne kroki, zrozumieć.
Ten wpis jest częścią serii Podstawy Playwright. Jeśli pominąłeś poprzedni post, przeczytaj najpierw Część 8 — Równoległość, sharding i Playwright gotowy na CI.
Wprowadzenie — debugowanie to umiejętność, nie zgadywanie
Najgorszy sposób debugowania padającego testu to wpatrywanie się w kod, zmiana czegoś, co “może pomóc”, ponowne uruchomienie testu i powtarzanie, aż przejdzie. To wolne, demoralizujące i nie uczy cię niczego o przyczynie źródłowej.
Najlepszy sposób debugowania padającego testu to obserwacja tego, co faktycznie się stało. Playwright daje ci narzędzia, aby zobaczyć dokładnie, co zrobiła przeglądarka, czego oczekiwał test i gdzie nastąpiła niezgodność. Dzięki tym narzędziom debugowanie staje się systematyczne: zbierz dowody, sformułuj hipotezę, zweryfikuj hipotezę, napraw przyczynę źródłową.
Ten post pokaże ci, jak używać narzędzi debugowania Playwright:
- Tryb UI — interaktywny eksplorator testów z podglądem przeglądarki na żywo
- Przeglądarka trace — debugger podróży w czasie z pełną historią sieci, DOM i akcji
- Tryb headed — oglądaj testy działające na żywo z DevTools przeglądarki
- Tryb
--debug— przejdź przez testy linia po linii jak tradycyjny debugger - Analiza artefaktów — zrzuty ekranu, wideo i logi z awarii CI
Pod koniec będziesz wiedział, jak diagnozować awarie testów szybciej niż ktokolwiek w twoim zespole.
Tryb UI: interaktywny eksplorator testów
Tryb UI to najlepiej strzeżony sekret Playwright. To interaktywny runner testów, który pokazuje ci:
- Podgląd przeglądarki na żywo podczas działania testu
- Oś czasu każdej akcji i asercji
- Żądania sieciowe, logi konsoli i snapshoty DOM
- Status pass/fail w czasie rzeczywistym dla każdego kroku
Uruchom tryb UI:
npx playwright test --ui
To otwiera interfejs oparty na przeglądarce. Wybierz test z paska bocznego, kliknij Run i oglądaj jego wykonywanie krok po kroku. Jeśli krok pada, oś czasu podświetla awarię i możesz sprawdzić stan DOM w tym dokładnym momencie.
Kiedy używać trybu UI
Użyj trybu UI, gdy:
- Piszesz nowy test — Uruchom go interaktywnie, aby sprawdzić, czy każdy krok działa zgodnie z oczekiwaniami
- Debugujesz lokalną awarię — Zobacz dokładnie, co robi przeglądarka przy każdym kroku
- Izolujesz niestabilny test — Uruchom test 10 razy w trybie UI i obserwuj, kiedy/gdzie pada
Kluczowe funkcje
1. Oś czasu akcji: Każdy page.goto(), page.click(), expect() jest logowany. Kliknij dowolną akcję, aby zobaczyć stan DOM w tym momencie.
2. Inspektor sieci: Zobacz każde żądanie HTTP, kod statusu i payload odpowiedzi. Jeśli twój test pada, ponieważ API zwraca 500, zobaczysz to tutaj.
3. Logi konsoli: Jeśli aplikacja loguje błędy do konsoli, pojawiają się w pasku bocznym trybu UI. Nie musisz sprawdzać terminala.
4. Zwolnione tempo: Spowolnij wykonywanie testu, aby zobaczyć animacje, stany ładowania i przejścia, które dzieją się zbyt szybko, aby je normalnie obserwować.
Przeglądarka trace: debugger podróży w czasie
Przeglądarka trace to najpotężniejsze narzędzie debugowania w Playwright. Trace to kompletne nagranie wykonania testu: każda akcja, każde żądanie sieciowe, każdy snapshot DOM, każdy log konsoli. Możesz przechodzić do przodu i do tyłu przez test, jakbyś tam był, gdy działał.
Generowanie traces
Skonfiguruj zbieranie trace w playwright.config.ts:
export default defineConfig({
use: {
trace: 'on-first-retry',
},
});
To generuje traces tylko dla testów, które padają i są ponawiane. Traces są duże (~2–10 MB każdy), więc nie generuj ich dla każdego testu.
Jeśli test pada lokalnie i chcesz sprawdzić trace:
npx playwright test --trace on
Otwieranie przeglądarki trace
Po tym, jak test pada, Playwright zapisuje trace do test-results/. Otwórz go:
npx playwright show-trace test-results/<test-name>/trace.zip
Lub jeśli przesłałeś traces z CI, pobierz artefakt i uruchom:
npx playwright show-trace trace.zip
Co możesz zobaczyć w trace
1. Oś czasu akcji: Pełna lista każdej akcji wykonanej przez test. Kliknij dowolną akcję, aby zobaczyć:
- Stan DOM przed akcją
- Stan DOM po akcji
- Użyty lokator
- Czas trwania akcji
2. Zrzuty ekranu przy każdym kroku: Trace zawiera zrzut ekranu przed i po każdej akcji. Jeśli przycisk się przesunął lub zniknął, zobaczysz to.
3. Karta sieci: Każde żądanie sieciowe z:
- URL żądania, metoda, nagłówki, payload
- Status odpowiedzi, nagłówki, body
- Informacje o czasie (jak długo trwało żądanie)
4. Logi konsoli: Każdy console.log(), console.error() i console.warn() z aplikacji.
5. Snapshoty: Kliknij “Before” lub “After” przy dowolnej akcji, aby zobaczyć pełny snapshot DOM. Możesz sprawdzać elementy, sprawdzać style i weryfikować stan aplikacji.
Przykład: debugowanie niestabilnego testu logowania
Wyobraź sobie, że ten test pada sporadycznie:
test('użytkownik może się zalogować', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', 'user@example.com');
await page.fill('input[name="password"]', 'haslo123');
await page.click('button:has-text("Zaloguj się")');
await expect(page).toHaveURL('/dashboard');
});
Przechodzi lokalnie, ale pada w CI. Otwórz trace z CI:
- Oś czasu akcji pokazuje: Test kliknął “Zaloguj się” i czekał, aż URL będzie
/dashboard - Karta sieci pokazuje: Żądanie POST logowania zwróciło 200, ale odpowiedź zajęła 8 sekund
- Snapshot przy awarii pokazuje: Strona nadal jest na
/login, pokazując spinner ładowania
Przyczyna źródłowa: Domyślny timeout (30 sekund) wygasł, ponieważ serwer zbyt długo odpowiadał. Test jest poprawny, ale aplikacja jest wolna.
Naprawa: Albo zoptymalizuj serwer, albo zwiększ timeout dla tej konkretnej akcji:
await page.click('button:has-text("Zaloguj się")');
await expect(page).toHaveURL('/dashboard', { timeout: 60000 }); // 60 sekund
Bez trace zgadywałbyś. Z trace wiedziałeś.
Tryb headed: oglądaj testy działające na żywo
Domyślnie Playwright działa w trybie headless (bez widocznej przeglądarki). Podczas debugowania przydatne jest zobaczyć przeglądarkę:
npx playwright test --headed
To otwiera prawdziwe okno przeglądarki i uruchamia test wewnątrz niego. Możesz obserwować każdą akcję w czasie rzeczywistym.
Kiedy używać trybu headed
- Debugowanie problemów wizualnych — Czy przycisk jest naprawdę ukryty, czy tylko poza ekranem?
- Zrozumienie problemów z czasem — Czy animacje zakłócają kliknięcia?
- Weryfikacja przepływów użytkownika — Czy test pasuje do tego, jak prawdziwy użytkownik wchodziłby w interakcję ze stroną?
Zwolnione tempo
Połącz tryb headed ze zwolnionym tempem, aby spowolnić wykonywanie testu:
npx playwright test --headed --slow-mo=1000
To dodaje 1-sekundowe opóźnienie między każdą akcją. Przydatne do obserwowania rozwijania się złożonych interakcji.
Tryb debug: przejdź przez testy linia po linii
Tryb debug zatrzymuje wykonywanie testu na początku i otwiera Playwright Inspector, wbudowany debugger krokowy:
npx playwright test --debug
Inspektor pozwala:
- Step over — Wykonaj następną linię i zatrzymaj się
- Step into — Jeśli następna linia to wywołanie funkcji, wejdź do niej
- Resume — Uruchom do następnego breakpointa lub końca testu
Możesz też ustawić breakpointy w swoim kodzie testowym:
test('użytkownik może się zalogować', async ({ page }) => {
await page.goto('/login');
await page.pause(); // Wykonanie zatrzymuje się tutaj
await page.fill('input[name="email"]', 'user@example.com');
await page.fill('input[name="password"]', 'haslo123');
await page.click('button:has-text("Zaloguj się")');
});
Gdy test dotrze do page.pause(), zatrzymuje się. Możesz sprawdzić stronę w przeglądarce, uruchomić polecenia w konsoli Playwright Inspector i wznowić, gdy będziesz gotowy.
Kiedy używać trybu debug
- Pisanie nowego testu — Zatrzymaj się po każdym kroku, aby sprawdzić, czy zadziałał
- Naprawianie padającego testu — Zatrzymaj się w punkcie awarii i sprawdź stan strony
- Izolowanie niestabilnego zachowania — Uruchom test wiele razy i zatrzymaj się, gdy pada
Analiza artefaktów CI
Gdy test pada w CI, nie masz dostępnego trybu UI ani debugowania headed. Zamiast tego polegasz na artefaktach: traces, wideo, zrzuty ekranu i logi.
Pobieranie artefaktów z GitHub Actions
Jeśli twój workflow CI przesyła artefakty (jak pokazano w części 8), pobierz je:
- Przejdź do nieudanego uruchomienia GitHub Actions
- Przewiń do Artifacts
- Pobierz
playwright-results-<shard>lubplaywright-report-<shard>
W środku znajdziesz:
trace.zip— Otwórz przeznpx playwright show-trace trace.zipvideo.webm— Oglądaj test w odtwarzaczu wideoscreenshot.png— Stan DOM przy awariistdout.txt/stderr.txt— Wyjście konsoli
Na co zwracać uwagę
1. Trace: Zawsze najpierw sprawdź trace. Ma najwięcej informacji.
2. Żądania sieciowe: Czy API zwróciło błąd? Czy żądanie było nieoczekiwanie wolne?
3. Błędy konsoli: Czy aplikacja zalogowała błędy JavaScript?
4. Czas: Czy test przekroczył timeout czekając na element? Czy akcja była wolniejsza niż oczekiwano?
5. Stan DOM: Czy element był obecny, ale niewidoczny? Czy był zasłonięty przez inny element?
Przykład: padający test w CI
Test pada z: Error: Timeout 30000ms exceeded waiting for locator('button:has-text("Wyślij")').click()
Krok 1: Pobierz trace z CI i otwórz go.
Krok 2: Sprawdź oś czasu. Test wywołał page.click('button:has-text("Wyślij")') i czekał 30 sekund.
Krok 3: Sprawdź snapshot. Przycisk jest obecny, ale ma disabled="true".
Krok 4: Sprawdź kartę sieci. API walidacji formularza zwróciło 200, ale 3 sekundy przed timeoutem testu.
Przyczyna źródłowa: Przycisk jest wyłączony, dopóki API walidacji nie odpowie. API jest wolne w CI, więc przycisk pozostaje wyłączony dłużej niż test czeka.
Naprawa: Poczekaj, aż przycisk będzie włączony przed kliknięciem:
await page.click('button:has-text("Wyślij")'); // Stare: pada, jeśli przycisk jest wyłączony
await page.waitForSelector('button:has-text("Wyślij"):not([disabled])');
await page.click('button:has-text("Wyślij")'); // Nowe: czeka na stan włączony
Lub użyj auto-waitingu Playwright z wyższym timeoutem:
await page.click('button:has-text("Wyślij")', { timeout: 60000 });
Izolowanie niestabilnych testów
Niestabilne testy to testy, które czasami przechodzą, a czasami padają bez zmian w kodzie. To najbardziej frustrujące awarie do debugowania, ponieważ są niedeterministyczne.
Typowe przyczyny niestabilności
1. Problemy z czasem — Test klika przycisk, zanim zostanie włączony, lub czeka na element, który jeszcze się nie pojawił.
2. Race conditions — Dwa testy modyfikują ten sam rekord bazy danych jednocześnie w równoległym wykonaniu.
3. Przejściowe awarie sieci — API losowo zwraca 500.
4. Interferencja animacji — Element się przesuwa, podczas gdy test próbuje go kliknąć.
5. Zanieczyszczenie testów — Test A zmienia stan aplikacji, od którego zależy Test B.
Debugowanie niestabilnych testów
Krok 1: Reprodukuj lokalnie
Uruchom test 20–50 razy, aby zobaczyć, czy pada lokalnie:
npx playwright test <test-name> --repeat-each=50
Jeśli pada, debuguj go w trybie UI lub z traces.
Krok 2: Sprawdź trace z CI
Jeśli pada tylko w CI, pobierz trace i porównaj:
- Żądania sieciowe (czy CI jest wolniejsze?)
- Błędy konsoli (czy CI ma problemy specyficzne dla środowiska?)
- Czas (czy CI przekroczyło timeout, który przechodzi lokalnie?)
Krok 3: Dodaj jawne waity
Zamień niejawne waity na jawne waity:
// Źle: Niejawny wait (Playwright czeka, ale może nie wystarczająco długo)
await page.click('button:has-text("Wyślij")');
// Dobrze: Jawny wait (upewnij się, że przycisk jest widoczny i włączony)
await page.waitForSelector('button:has-text("Wyślij"):not([disabled])');
await page.click('button:has-text("Wyślij")');
Krok 4: Izoluj współdzielony stan
Jeśli test modyfikuje dane, od których zależą inne testy, albo:
- Użyj
.serial(), aby uruchamiać testy sekwencyjnie - Daj każdemu testowi własne dane testowe (unikalne konta użytkowników, unikalne rekordy)
Najlepsze praktyki debugowania
1. Zawsze najpierw sprawdź trace
Trace ma więcej informacji niż jakikolwiek inny artefakt. Zacznij od tego.
2. Reprodukuj lokalnie przed zgadywaniem
Nie zmieniaj kodu na podstawie logu awarii CI. Pobierz trace, reprodukuj problem i zweryfikuj swoją naprawę.
3. Używaj page.pause() obficie podczas pisania testów
Podczas pisania nowego testu zatrzymuj się po każdym kroku, aby sprawdzić, czy zadziałał:
await page.goto('/login');
await page.pause(); // Sprawdź, czy strona się załadowała
await page.fill('input[name="email"]', 'user@example.com');
await page.pause(); // Sprawdź, czy email został wypełniony
Usuń pauzy, gdy test jest stabilny.
4. Nie ignoruj niestabilnych testów
Śledź metryki niestabilnych testów. Jeśli test jest niestabilny > 5% czasu, zbadaj natychmiast. Niestabilne testy podważają zaufanie do pakietu testów.
5. Dodaj kontekstowe logowanie
Jeśli test pada w CI, ale przechodzi lokalnie, dodaj logowanie, aby zrozumieć środowisko:
test('użytkownik może się zalogować', async ({ page }) => {
console.log('Rozpoczynam test logowania');
await page.goto('/login');
console.log('Nawigowano do /login');
await page.fill('input[name="email"]', 'user@example.com');
console.log('Wypełniono email');
await page.fill('input[name="password"]', 'haslo123');
console.log('Wypełniono hasło');
await page.click('button:has-text("Zaloguj się")');
console.log('Kliknięto przycisk logowania');
});
To pomaga zawęzić, gdzie test pada.
Typowe scenariusze debugowania
Scenariusz 1: “Element nie jest widoczny”
Błąd: Error: locator('button').click() — element is not visible
Kroki debugowania:
- Otwórz trace
- Sprawdź snapshot przy awarii — czy element faktycznie jest obecny?
- Sprawdź CSS — czy ma
display: nonelubvisibility: hidden? - Sprawdź z-index — czy inny element go zasłania?
Typowa naprawa: Poczekaj, aż element będzie widoczny:
await page.waitForSelector('button', { state: 'visible' });
await page.click('button');
Scenariusz 2: “Timeout czekając na element”
Błąd: Error: Timeout 30000ms exceeded waiting for locator('div.results')
Kroki debugowania:
- Sprawdź trace — czy element kiedykolwiek się pojawił?
- Sprawdź żądania sieciowe — czy API zawiodło lub zwróciło późno?
- Sprawdź logi konsoli — czy JavaScript rzucił błąd?
Typowa naprawa: Albo napraw aplikację (opóźnienie API, błąd JS), albo zwiększ timeout:
await page.waitForSelector('div.results', { timeout: 60000 });
Scenariusz 3: Test przechodzi lokalnie, pada w CI
Kroki debugowania:
- Pobierz trace z CI
- Porównaj czas sieci — czy CI jest wolniejsze?
- Porównaj zmienne środowiskowe — czy CI brakuje konfiguracji?
- Sprawdź race conditions — czy CI uruchamia więcej testów równolegle?
Typowa naprawa: CI jest często wolniejsze. Zwiększ timeouty lub zoptymalizuj aplikację.
Podsumowanie
Debugowanie to nie zgadywanie. Playwright daje ci obserwowalność: traces, zrzuty ekranu, logi sieci, wyjście konsoli. Użyj tych narzędzi, aby zobaczyć, co się stało, sformułować hipotezę i zweryfikować swoją naprawę.
Kluczowe wnioski:
- Użyj trybu UI podczas pisania lub debugowania testów lokalnie
- Użyj przeglądarki trace, aby zbadać awarie CI
- Użyj trybu headed, aby oglądać testy działające na żywo
- Użyj trybu debug, aby przechodzić przez testy linia po linii
- Zawsze pobieraj artefakty z CI i sprawdzaj trace
:::tip[Zbuduj checklistę debugowania] Gdy test pada, postępuj według tej checklisty:
- Pobierz trace
- Sprawdź oś czasu akcji — który krok padł?
- Sprawdź snapshot — jaki był stan DOM?
- Sprawdź kartę sieci — jakieś nieudane żądania?
- Sprawdź logi konsoli — jakieś błędy JavaScript?
- Sformułuj hipotezę i zweryfikuj ją lokalnie :::
Zadanie na ten tydzień: Następnym razem, gdy test pada, nie zgaduj. Otwórz przeglądarkę trace i systematycznie badaj. Zmierz się. Po kilku awariach będziesz debugować szybciej niż ktokolwiek, kto po prostu zgaduje i ponownie uruchamia testy.
Następny w tej serii: Część 10 — Praktyki Playwright, które przetrwają wzrost