• test
  • test e2e
  • test integracyjny
  • dev
  • produktywność
  • jest
  • puppeteer

Szybki przewodnik po pisaniu testów end-to-end z użyciem jest-puppeteer

Ten artykuł dostarcza szybkiego przewodnika po pisaniu efektywnych testów end-to-end z użyciem jest-puppeteer, podkreślając proces konfiguracji, najczęściej używane API oraz praktyczne scenariusze testowe na przykładzie prostego zadania do zrobienia.

Yijun
Yijun
Developer

W ramach naszego zobowiązania do zapewnienia jakości Logto i ciągłego doskonalenia stosujemy jest-puppeteer do testów automatycznych end-to-end. Pozwala nam to na szybką iterację procesu rozwoju Logto bez zakłóceń.

W tym artykule podzielimy się naszym doświadczeniem w pisaniu efektywnych skryptów testowych jest-puppeteer na przykładzie prostego zadania do zrobienia. Naszym celem jest pomóc ci szybko rozpocząć pisanie kodu testowego jest-puppeteer do twoich własnych projektów.

Wprowadzenie do testowania end-to-end z użyciem jest-puppeteer

Testowanie end-to-end to sposób na zapewnienie, że twoja aplikacja działa poprawnie z perspektywy użytkownika. Aby to osiągnąć, korzystamy z dwóch podstawowych narzędzi: Jest i Puppeteer.

Jest to popularny framework do testowania JavaScriptu, który oferuje przyjazne dla użytkownika API do pisania testów i asercji. Jest powszechnie używany do testowania jednostkowego i integracyjnego.

Puppeteer to biblioteka Node.js opracowana przez zespół Chrome, która oferuje wysokopoziomowe API do kontrolowania przeglądarek Chrome lub Chromium bez interfejsu graficznego. Jest to idealne narzędzie do automatyzacji interakcji z przeglądarką w testach end-to-end.

jest-puppeteer to preset do Jest, który umożliwia testowanie end-to-end z użyciem Puppeteer. Oferuje proste API do uruchamiania nowych instancji przeglądarek oraz interakcji z nimi na stronach internetowych.

Teraz, gdy masz podstawowe zrozumienie niezbędnych narzędzi, zanurzmy się w konfigurowanie środowiska testowego.

Konfigurowanie środowiska testowego

Konfigurowanie środowiska testowego do testów end-to-end z użyciem jest-puppeteer to prosty proces, obejmujący trzy główne kroki:

  1. Zainstaluj zależności

W swoim projekcie (lub tworząc nowy projekt) otwórz terminal i uruchom:

Następnie przejdź do node_modules/puppeteer i zainstaluj Chromium (które jest potrzebne do Puppeteer):

  1. Skonfiguruj Jest

Następnie musisz skonfigurować Jest do płynnej pracy z Puppeteer.

Utwórz plik konfiguracyjny Jest (np. jest.config.js) w katalogu głównym projektu, jeśli go jeszcze nie masz. W tym pliku konfiguracyjnym, określ jest-puppeteer jako preset:

Możesz dostosować inne ustawienia Jest w tym pliku, jeśli to konieczne. Więcej informacji na temat dostosowywania konfiguracji Jest znajdziesz w dokumentacji konfiguracji Jest.

  1. Napisz swoje testy

Twórz pliki testowe w swoim projekcie, zazwyczaj nazywane z rozszerzeniem .test.js. Jest automatycznie wykryje i uruchomi te pliki testowe.

Oto przykład z dokumentacji Jest:

Następnie uruchom polecenie, aby wykonać test:

Stosując te trzy kroki, będziesz mieć dobrze skonfigurowane środowisko testowe do wykonywania testów end-to-end z użyciem jest-puppeteer.

Jednak pamiętaj, że to tylko podstawowy przykład. Po więcej szczegółowych informacji na temat konfiguracji środowiska odsyłam do odpowiedniej dokumentacji:

Najczęściej używane API

W dalszych krokach będziemy polegać na API dostarczonych przez Jest, Puppeteer oraz jest-puppeteer, aby pomóc nam w testach.

Jest głównie oferuje API do organizowania testów i sprawdzania oczekiwanych wyników. Możesz zapoznać się ze szczegółami w dokumentacji.

API Puppeteer służą głównie do interakcji z przeglądarkami, a ich wsparcie dla testowania może nie być tak oczywiste. Możesz zapoznać się z dokumentacją Puppeteer, aby zrozumieć, jakie funkcje oferuje. W naszych kolejnych przykładach testowych omówimy kilka powszechnych przypadków użycia.

Ponieważ API Puppeteer nie były pierwotnie zaprojektowane do testowania, pisanie testów z ich użyciem może być wyzwaniem. Aby uprościć proces pisania testów Puppeteer, jest-puppeteer zawiera wbudowaną bibliotekę zwaną expect-puppeteer. Oferuje ona zestaw zwięzłych i przyjaznych interfejsów API. Biblioteka ta jest szczegółowo opisana w dokumentacji, która wprowadza szereg użytecznych funkcji, jak pokazano w poniższych przykładach:

W kolejnych przykładach połączymy API dostarczone przez te biblioteki, aby zrealizować nasze scenariusze testowe.

Teraz nadszedł czas, aby rozpocząć naukę pisania kodu testowego z wykorzystaniem naszego prostego zadania do zrobienia.

Prosta aplikacja to-do

Załóżmy, że mamy prostą aplikację to-do działającą pod adresem http://localhost:3000, a główny kod HTML tej aplikacji wygląda następująco:

Podczas testowania end-to-end chcemy objąć następujące scenariusze:

  1. Po uzyskaniu dostępu do adresu URL aplikacji, aplikacja wraz z danymi powinna załadować się poprawnie.
  2. Przycisk sprawdzania pozycji powinien być możliwy do kliknięcia.
  3. Kliknięcie zewnętrznego linku w notatkach pozycji powinno otworzyć link w nowej karcie.
  4. Powinno być możliwe dodanie pozycji z formularza.

Później napiszemy kod testowy, aby zweryfikować te scenariusze.

Spodziewaj się, że aplikacja i jej dane zostały załadowane

Uważamy, że aplikacja została pomyślnie załadowana, gdy spełnione są następujące warunki:

  1. Element z ID „app” powinien istnieć na stronie po uzyskaniu dostępu do adresu URL aplikacji.
  2. Dane wewnątrz aplikacji powinny zostać poprawnie wyrenderowane.

Dlatego napisaliśmy poniższy kod testowy:

W kodzie:

  • page.goto jest odpowiednikiem wpisania „http://localhost:3000” w przeglądarce, co pozwala nawigować do dowolnego adresu URL.
  • page.waitForSelector jest używane do oczekiwania na dopasowanie określonego selektora CSS do konkretnego elementu, z domyślnym czasem oczekiwania wynoszącym 30 sekund.
  • expect(page).toMatchElement pochodzi z biblioteki expect-puppeteer, jest podobne do page.waitForSelector, ale również obsługuje dopasowanie tekstu wewnątrz elementu. Dodatkowo, domyślny timeout dla toMatchElement wynosi tylko 500ms.

Może się to wydawać idealne na pierwszy rzut oka, ale podczas wykonywania tego testu i wdrażania go w środowisku CI czasami dochodzi do awarii po wielokrotnym wykonaniu. Wiadomość o niepowodzeniu wskazuje:

Na podstawie informacji o błędzie i faktu, że ten test przechodzi większość czasu, możemy wnioskować, że dane żądane przez aplikację mogą nie zawsze wrócić w ciągu pierwszych 500ms po załadowaniu aplikacji. W związku z tym chcemy poczekać na załadowanie danych przed dokonaniem asercji.

Typowo istnieją dwie powszechne metody osiągnięcia tego:

  1. Zwiększanie czasu oczekiwania na asercję

Na podstawie wiadomości o błędzie możemy zobaczyć, że domyślny czas oczekiwania dla toMatchElement jest ustawiony na 500ms. Możemy zwiększyć czas oczekiwania, dodając opcję timeout do funkcji w ten sposób:

To podejście może zmniejszyć częstotliwość niepowodzeń testów do pewnego stopnia, ale nie rozwiązuje problemu całkowicie, ponieważ nie wiemy, ile czasu zajmie załadowanie danych.

Dlatego stosujemy tę metodę tylko wtedy, gdy jesteśmy pewni wymaganego czasu oczekiwania, na przykład w scenariuszach takich jak „podpowiedź pojawia się po najechaniu na element przez więcej niż 2 sekundy.”

  1. Oczekiwanie na zakończenie żądania sieciowego przed asercją

To jest właściwe podejście. Chociaż nie wiemy, ile czasu zajmie żądanie danych, zawsze bezpiecznym wyborem jest oczekiwanie na zakończenie żądania sieciowego. W tym momencie możemy użyć page.waitForNavigation({ waitUntil: 'networkidle0' }), aby czekać na zakończenie żądań sieciowych:

W ten sposób, zanim wykonamy asercję dla aplikacji i jej załadowanych danych, możemy upewnić się, że nasze żądania sieciowe zostały zakończone. To zapewnia, że test konsekwentnie przynosi prawidłowe wyniki.

Spodziewaj się kliknięcia określonego przycisku

Następnie testujemy funkcjonalność kliknięcia przycisku „sprawdź” wewnątrz pozycji.

W przykładowej aplikacji zauważyliśmy, że wszystkie pozycje mają taką samą strukturę. Ich przyciski „sprawdź” mają selektor CSS li[class$=item] > button, a tekst przycisku to zawsze „Sprawdź”. Oznacza to, że nie możemy bezpośrednio określić, który przycisk „sprawdź” pozycji kliknąć. W związku z tym potrzebujemy nowego rozwiązania.

Załóżmy, że chcemy kliknąć przycisk „sprawdź” dla pozycji „Przeczytaj dokument wprowadzający Logto”. Możemy to rozłożyć na dwa kroki:

  1. Najpierw uzyskaj referencję do tej konkretnej pozycji.
  2. Następnie kliknij przycisk „sprawdź” znajdujący się w tej pozycji.

Jak pokazano w kodzie, używamy funkcji toMatchElement, aby dopasować pozycję z itemName ustawionym na „Przeczytaj dokument wprowadzający Logto”. Następnie używamy referencji readDocItem, aby kliknąć przycisk z zawartością tekstową „Sprawdź” poniżej.

Warto zauważyć, że podczas uzyskiwania readDocItem używamy selektora CSS li[class$=item]:has(div[class$=itemName]). Ten selektor dopasowuje element li korzenia pozycji, a nie itemName wewnątrz, ponieważ później zamierzamy kliknąć button pod tagiem li.

Użycie expect(readDocItem).toClick jest podobne do toMatchElement. W przykładowym kodzie przekazujemy { text: 'Sprawdź' }, aby dokładniej dopasować textContent przycisku. Jednak możesz zdecydować, czy chcesz dopasowywać zawartość tekstową przycisku, czy nie, w zależności od swoich potrzeb.

Następnie chcemy przetestować, czy możemy otworzyć zewnętrzny link w nowej karcie po kliknięciu go w notatkach pozycji.

W przykładowej aplikacji zauważyliśmy, że pozycja „Przeczytaj dokument wprowadzający Logto” ma zewnętrzny link prowadzący do dokumentacji Logto w swoich notatkach. Oto nasz kod testowy:

W kodzie używamy toClick, aby kliknąć link wewnątrz itemNotes.

Następnie używamy browser.waitForTarget, aby uchwycić nowo otwartą kartę z adresem URL „https://docs.logto.io/docs/tutorials/get-started.”

Po uzyskaniu karty używamy target.page(), aby uzyskać instancję strony dokumentacji Logto i dalej sprawdzać, czy strona została załadowana.

Pamiętaj również, aby zamknąć nowo otwartą stronę po zakończeniu naszego testu.

Spodziewaj się, że element zostanie utworzony z formularza

Teraz chcemy przetestować funkcjonalność dodawania elementu.

Musimy wypełnić nazwę elementu i notatki elementu w formularzu, kliknąć przycisk „Dodaj” i sprawdzić, czy treść, którą dodaliśmy, pojawia się na liście:

Zauważ, że używamy funkcji expect(page).toFill(inputSelector, content), aby wypełnić treść formularza. Funkcja ta zastępuje całą zawartość w polu wprowadzania content.

Jeśli chcesz dodać kilka znaków do pola wprowadzania bez zastępowania istniejącej zawartości, możesz użyć page.type(selector, content), aby bezpośrednio dodać żądaną treść do pola wprowadzania.

Jednak gdy mamy wiele pól do wypełnienia w naszym formularzu, wielokrotne wywoływanie toFill nie jest wygodne. W takim przypadku możemy użyć następującego podejścia, aby wypełnić całą zawartość w jednym wywołaniu:

Podany klucz obiektu to atrybut name elementu wprowadzania.

Podsumowanie

Omówiliśmy prosty przykład, aby przedstawić powszechne wymagania dotyczące testowania i techniki pisania odpowiedniego kodu, gdy używamy jest-puppeteer do testowania end-to-end. Mamy nadzieję, że pomoże ci to szybko zrozumieć podstawy pisania kodu testowego jest-puppeteer.