Środowiska testujące

W tym rozdziale opisujemy czynniki wpływające na środowisko testujące i nasze rekomendacje dla niektórych scenariuszy.

Narzędzia uruchamiające testy (ang. test runners)

Narzędzia uruchamiające testy, jak np. Jest, mocha czy ava, pozwalają tworzyć zestawy testowe przy użyciu samego JavaScriptu, a także uruchamiać je jako część procesu tworzenia oprogramowania. Dodatkowo, testy mogą być uruchamiane w ramach procesu “ciągłej integracji” (ang. Continuous Integration, CI).

  • Jest ma wysoką kompatybilność z projektami reactowymi i obsługuje wiele przydatnych funkcjonalności, jak mockowanie modułów czy sztuczne timery. Dobrze współpracuje również z jsdom. Jeśli używasz Create React App, domyślnie masz już dostęp do Jesta z odpowiednią konfiguracją.
  • Biblioteki takie jak mocha świetnie spisują się w środowiskach przeglądarkowych, dzięki czemu mogą okazać się pomocne w przypadku niektórych testów.
  • Testy kompleksowe end-to-end, stosowane w przypadku dłuższych ścieżek rozciągających się na wiele stron aplikacji, wymagają innej konfiguracji.

Mockowanie warstwy renderującej

Testy często uruchamiane są w środowiskach niemających dostępu do prawdziwej warstwy renderującej, np. przeglądarki. Zalecamy więc symulowanie zachowań przeglądarki za pomocą jsdom, niewielkiej implementacji przeglądarki działającej na Node.js.

W większości przypadków jsdom zachowuje się jak prawdziwa przeglądarka, lecz nie posiada niektórych funkcjonalności, jak np. generowanie układu strony czy nawigacja. Mimo tego paczka z powodzeniem sprawdza się dla większości komponentów pisanych pod przeglądarkę; działa szybciej niż uruchamianie przeglądarki dla każdego testu z osobna. Ponadto, uruchamia ona testy w tym samym procesie, umożliwiając pisanie kodu sprawdzającego wyrenderowane drzewo DOM.

Podobnie jak prawdziwa przeglądarka, jsdom pozwala na modelowanie interakcji użytkownika; testy mogą wywoływać zdarzenia na węzłach DOM, a następnie obserwować i sprawdzać wyniki tych akcji (przykład).

Przy takiej konfiguracji można śmiało napisać większość testów dla UI: Jest jako narzędzie uruchamiające testy, jsdom służący do renderowania, interakcje użytkownika określone jako sekwencje zdarzeń przeglądarkowych - a to wszystko “spięte” za pomocą funkcji pomocniczej act() (przykład). Spora część testów samego Reacta jest napisana przy użyciu powyższej kombinacji.

Jeśli piszesz bibliotekę, która testuje głównie zachowania charakterystyczne dla przeglądarki, a w dodatku wymaga natywnych mechanizmów przeglądarki, jak generowanie układu strony, zalecamy skorzystanie z frameworka mocha.

W środowisku, które uniemożliwia symulowanie modelu DOM (np. podczas testowania komponentów napisanych w React Native na Node.js), możesz skorzystać z narzędzi do symulowania zdarzeń do symulowania interakcji z elementami. Alternatywnie możesz skorzystać z funkcji fireEvent dostarczonej przez @testing-library/react-native.

Frameworki jak Cypress, puppeteer czy webdriver służą do uruchamiania testów end-to-end.

Mockowanie funkcji

Podczas pisania testów czasami chcemy podmienić części naszego kodu, które nie posiadają odpowiedników w używanym przez nas środowisku (np. sprawdzanie statusu navigator.onLine w Node.js). Testy mogą również śledzić niektóre funkcje i obserwować, jak pozostałe części kodu wchodzą z nimi w interakcje. Pomocna okazuje się wtedy możliwość wybiórczego zastąpienia niektórych funkcji wersjami odpowiednimi dla testów.

Szczególnie przydatne okazuje się to przy pobieraniu danych. Zazwyczaj lepiej w testach używać “sztucznych” danych, aby uniknąć spowolnień czy niestabilności z powodu odwołań do prawdziwego API (przykład). Dzięki takiemu zabiegowi testy są przewidywalne. Biblioteki typu Jest czy sinon wspierają mockowanie funkcji. W przypadku testów end-to-end, mockowanie sieci może okazać się trudniejsze, choć należy tego unikać i zamiast tego testować korzystając z prawdziwego API.

Mockowanie modułów

Niektóre komponenty mają zależności w modułach, które mogą nie działać w środowisku testowym lub które zwyczajnie nie są istotne z punktu widzenia naszych testów. Warto wtedy zastąpić te moduły czymś odpowiednim dla danego przypadku (przykład).

W Node.js mockowanie modułów jest wspierane np. przez bibliotekę Jest. Można to również osiągnąć z pomocą paczki mock-require.

Mockowanie timerów

Komponenty mogą korzystać z funkcji opartych na czasie, np. setTimeout, setInterval czy Date.now. W środowisku testowym warto zamieniać tego typu funkcje na ich zastępniki, które pozwalają ręcznie “sterować czasem”. Testy korzystające z timerów nadal będą wykonywać się w odpowiedniej kolejności, ale zdecydowanie szybciej (przykład). Większość frameworków, również Jest, sinon oraz lolex, pozwalają na mockowanie timerów w testach.

Niekiedy jednak możesz chcieć skorzystać z prawdziwych timerów, na przykład, gdy testujesz animację lub interakcję z endpointem, który zależy od czasu (np. ogranicza częstość odpytywania API). Biblioteki zawierające sztuczne timery pozwalają na łatwe włączanie i wyłączanie tego mechanizmu dla każdego zestawu testowego lub pojedynczego testu. Dzięki temu możesz zdecydować, jak poszczególne testy mają być uruchamiane.

Testy end-to-end

<<<<<<< HEAD Testy end-to-end są efektywne przy testowaniu dłuższych sekwencji interakcji, zwłaszcza jeśli są one krytyczne dla twojego produktu (np. płatność czy rejestracja). W takich przypadkach konieczne jest przetestowanie, jak przeglądarka renderuje całą aplikację, jak pobiera dane z API, korzysta z sesji i ciasteczek lub nawiguje pomiędzy poszczególnymi stronami. Możesz w nich sprawdzać nie tylko stan drzewa DOM, lecz także sterujące nim dane (np. weryfikując, czy dane zostały zapisane w bazie danych). ======= End-to-end tests are useful for testing longer workflows, especially when they’re critical to your business (such as payments or signups). For these tests, you’d probably want to test how a real browser renders the whole app, fetches data from the real API endpoints, uses sessions and cookies, navigates between different links. You might also likely want to make assertions not just on the DOM state, but on the backing data as well (e.g. to verify whether the updates have been persisted to the database).

99a18287c163e328f87709cb224742ccac3e113a

Do takich scenariuszy możesz skorzystać z frameworka Cypress lub biblioteki puppeteer, które pozwalają nawigować pomiędzy stronami i sprawdzać rezultaty nie tylko w samej przeglądarce, ale potencjalnie również na backendzie.