Playwright umie poczekać, aż locator zacznie pasować. To nie to samo co poczekać, aż aplikacja zrobi to, o czym myślisz, że testujesz. Jeśli warunek jest spełniony już na ekranie ładowania, na domyślnej zakładce albo na adresie, na którym i tak jesteś, kliknięcie może być no-op, a suite i tak będzie zielone.
Ten tekst jest o tym sygnale, nie o liczbie scenariuszy.
Po co ten tekst
Zespoły liczą e2e tak, jak liczą joby w CI: zielone albo czerwone. Zielone ma znaczyć „ścieżka użytkownika działa”. Czerwone ma znaczyć „zmiana coś zepsuła albo środowisko jest chore”.
W praktyce część zieleni nic nie mówi o kliknięciu. Asercja była prawdziwa przed akcją. Auto-wait w Playwright wtedy nie pomaga: nie ma na co czekać, warunek już zachodzi.
To jest kuzyn dwóch innych kosztów, o których pisałem osobno. Czas na zrozumienie zmiany, zanim w ogóle wybierzecie zakres, opisałem we wpisie o ukrytym podatku na PR. Pipeline, który krzyczy fałszywym alarmem, we wpisie o hałaśliwym CI. Tu trzeci biegun: test, który milczy, bo nigdy nie zadał trudnego pytania.
Widziałem to na demonstracyjnym stosie SPA plus API (klon klasycznego RealWorld): po review suite był szeroki, a kilka asercji i tak przeszłoby, gdyby zakładka się nie przełączyła albo lista jeszcze wisiała na spinnerze.
Trzy asercje, które już były prawdziwe
Schemat powtarza się w SPA z zakładkami, Suspense i formularzami, które po zapisie zostają na tym samym URL.
| Objaw | Co test „myśli” | Co jest prawdą wcześniej | Lepszy hak |
|---|---|---|---|
toHaveURL na stronie ustawień |
zapis zrobił redirect | już tam byliście; redirect na ten sam path nie jest nawigacją | czekaj na PUT profilu albo na zmianę etykiety przycisku |
| kafelek artykułu „jest na kanale osobistym” | kliknięto właściwą zakładkę | ten sam kafelek widać na kanale globalnym | najpierw feed=personal w adresie, potem kafelek |
toHaveCount(0) po usunięciu |
listy nie ma tego tytułu | podczas spinnera nie ma żadnego kafelka | poczekaj, aż pojawi się pusty stan albo jakikolwiek preview, potem dopiero licz zero |
Żaden z tych testów nie jest „zły, bo Playwright”. Playwright zrobił to, o co go poproszono: poczekał, aż locator będzie prawdziwy. Poproszono o warunek, który umie być prawdziwy za wcześnie.
Krótki przykład trzeciego wiersza. Po usunięciu artykułu aplikacja wraca na home. Feed ładuje się w Suspense. W DOM nie ma jeszcze data-test="article-preview". expect(preview).toHaveCount(0) przechodzi od razu. Późniejsze wejście na stary slug i komunikat „nie udało się wczytać” może uratować scenariusz. Sama asercja na liście nie udowadnia usunięcia.
Czwarty wariant: czekasz na zły request
Inny klasyk: waitForResponse, którego predykat patrzy na fragment URL-a.
Ścieżka API .../follow i chunk bundlera, którego nazwa zawiera follow, to dla url.includes('follow') to samo. Test „doczekał się odpowiedzi”, od razu nawigował dalej i urwał prawdziwe POST. Na backendzie w logu zostaje anulowane żądanie albo 500, na froncie przycisk zdążył optymistycznie zmienić etykietę.
Tu też auto-wait nie kłamie. Kłamie matcher. Czekaj na metodę HTTP plus path API (POST .../follow), nie na dowolny URL z tym słowem.
Co z tego wynika na review
Zanim dodacie kolejny scenariusz, warto na każdej asercji zapytać: czy to mogłoby przejść, gdyby kliknięcie nic nie zrobiło?
Jeśli tak, haki, które zwykle działają:
- Stan, którego nie było przed akcją: parametr w adresie (
feed=personal,favorited=,tag=), etykieta przycisku, która zmienia się dopiero po sukcesie. - Request, który naprawdę mutuje:
PUTprofilu,POSTfollow,DELETEfavorite. Nie redirect na ten sam path i nie chunk webpacka. - Lista, która najpierw musi się pojawić: pusty komunikat albo jakikolwiek preview, i dopiero potem „tego tytułu nie ma”.
Osobna, krótsza reguła z tego samego labu, bo daje inny rodzaj fałszywego dowodu: jeśli test nie jest o logowaniu, nie udowadniaj sesji formularzem logowania. Zasiej użytkownika przez API, wstrzyknij token, a Playwrightowi zostaw kliknięcia. W drugą stronę też: nie followuj i nie komentuj przez API, a potem „sprawdzaj UI”. Wtedy e2e mierzy odczyt, nie ścieżkę.
Modele dobrze produkują locatory i toBeVisible. Gorzej pytają, czy warunek nie był spełniony już na starcie. To nie argument przeciwko agentom w QA. To argument, żeby review testów miało to jedno pytanie na checkliście, obok „czy to w ogóle e2e, czy hit API”.
Pipeline, który testuje nie ten kontrakt
Jest jeszcze zielony job, który kłamie z innego powodu. Suite e2e odpalany przeciwko main frontendu nie zobaczy poprawki kontraktu, która siedzi na otwartym PR-ze. Dostaniecie czerwony settings, bo puste hasło nadal idzie w PUT, albo odwrotnie: zmergujecie e2e przed produktem i main testów będzie czerwony, dopóki druga usługa nie wyląduje.
To nie flake locatora. To kolejność merge. W split stacku (osobne repo UI, osobne API, osobne e2e) najpierw kontrakt, potem test, który na tym kontrakcie stoi. Inaczej review „test jest zły” marnuje ten sam budżet uwagi, o którym pisałem przy hałaśliwym CI.
Zamknięcie
Dobry e2e ma mówić: ta ścieżka użytkownika zareagowała na akcję. Gdy asercja jest prawdziwa już na spinnerze, suite dokłada pewność zamiast dowodu - analogicznie do pipeline’u, który dokłada wątpliwości zamiast werdyktu.
Nie trzeba na to drugiego frameworka. Trzeba na review jednego pytania: czy to przejdzie, jeśli klik nie zadziała. Jeśli tak, najpierw poprawcie hak, zanim dokładacie kolejny happy path.
