Asercja, która była prawdziwa, zanim kliknąłeś

Asercja, która była prawdziwa, zanim kliknąłeś

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: PUT profilu, POST follow, DELETE favorite. 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.