Testowanie frontendu: testy statyczne vs jednostkowe vs integracyjne vs E2E
Mateusz Wójcik
20 lip 2020・5 min czytania
Spis treści
Czym są testy statyczne?
Testy statyczne z ESLint
Testy statyczne z TypeScriptem
Testy jednostkowe frontendu
Testy integracyjne frontendu
Testy end-to-end (E2E) frontendu
Na koniec o testowaniu frontendu
Testowanie frontendu to duża część procesu tworzenia oprogramowania. Korzyści z dodawania testów do naszych aplikacji są oczywiste, ale łatwo się w tym pogubić. Istnieje wiele rodzajów testów, a ich definicje mogą się różnić w zależności od rozmówcy, używanych narzędzi i sposobów testowania kodu. W sieci znajdziesz kilka przykładów „map drogowych” testowania frontendu, jak testing pyramid czy testing dorito. Mnie jednak najbardziej przekonał Testing Trophy Kenta C. Dodds’a. Przejdźmy przez każdy segment tego Testing Trophy i zobaczmy jego plusy i minusy.
Czym są testy statyczne?
Podstawą testowania frontendu powinny być testy statyczne. Obejmują lintery (np. ESLint) i statyczne sprawdzanie typów (np. TypeScript).
Testy statyczne z ESLint
ESLint jest bardzo popularny w ekosystemie JavaScript. To narzędzie skanuje kod i wskazuje potencjalne problemy, np. próbujesz użyć zmiennej, której jeszcze nie zadeklarowałeś, albo wywołujesz funkcję, której nie zaimportowałeś. ESLint potrafi część z nich automatycznie naprawić. Łatwo go skonfigurować, a gotowe konfiguracje, takie jak eslint:recommended czy eslint-config-react-app, pozwalają szybko zacząć pracę.
Instalacja potrzebnych paczek zajmuje kilka minut. Warto ustawić IDE tak, aby uruchamiało ESLint przy każdym zapisie pliku, albo użyć narzędzi takich jak Husky i Lint-Staged, by odpalać go przy każdym commicie. Dzięki temu każda zmiana trafiająca do zdalnego repozytorium będzie sprawdzona i – o ile to możliwe – automatycznie poprawiona przez ESLint.
Testy statyczne z TypeScriptem
TypeScript wymaga nieco więcej pracy, bo to nadzbiór JavaScriptu, który rozszerza możliwości JS. Chodzi głównie o statyczne sprawdzanie typów, dostępne w wielu językach programowania, ale niestety nie w czystym JavaScript.
Pozwala określać typy zmiennych i parametrów funkcji. Na przykład w funkcji function
sum(a: number, b: number): number { return a + b; }określamy, że parametry „a” i „b” mają być liczbami, a funkcja zwróci liczbę.
W tym przykładzie mówimy więc, że parametry „a” i „b” powinny być liczbami, a nasza funkcja zwróci liczbę. Gdy spróbujesz przekazać do niej łańcuch znaków, TypeScript to wychwyci i zgłosi błąd. Praca z TypeScriptem bywa najtrudniejszą częścią testów statycznych, bo wymaga opanowania nowej składni.
Testy jednostkowe frontendu
Testy jednostkowe są dość proste, bo skupiają się na odseparowanych częściach aplikacji. Brak zewnętrznych zależności, brak frameworków.
Mogą testować nawet pojedynczą funkcję. Na przykład możemy dodać test jednostkowy dla funkcji „sum” w ten sposób:
test('sum function', () => { expect(sum(1, 3)).toBe(4); });W tym teście oczekujemy, że suma 1 i 3 da wynik 4.
Testy jednostkowe są czytelne i można je niemal „czytać po angielsku”: „Oczekuj, że sum z argumentami 1 i 3 zwróci 4”. Zgodnie z Testing Trophy to dobry punkt wyjścia do bardziej złożonych testów. Jeśli interesuje Cię ten typ testów, sprawdź bibliotekę Jest. Nie wymaga rozbudowanej konfiguracji i zazwyczaj działa świetnie, o ile masz wystarczająco dużo helperów, by przetestować wszystko, czego potrzebujesz. Możesz też tworzyć bardziej złożone przypadki testowe, mockując zapytania i odpowiedzi API.
W trakcie rozwoju produktu skup się na testowaniu kluczowych części aplikacji oraz jej logiki. W przeciwnym razie łatwo zacząć testować szczegóły implementacyjne, co zwykle jest zbędne. Na tym etapie masz już pokrycie testami statycznymi, a część aplikacji ma testy jednostkowe. Czas na kolejny rodzaj testów – integracyjne.
Testy integracyjne frontendu
Testy integracyjne sprawdzają, czy różne elementy aplikacji działają razem. Z biznesowego punktu widzenia są kluczowe. Użytkowników nie obchodzi, czy pojedyncza funkcja działa – chcą wiedzieć, czy mogą korzystać z całej aplikacji, i tu testy integracyjne błyszczą.
React Testing Library to podstawowe narzędzie do testów integracyjnych. Jej główny cel to testowanie w sposób zbliżony do rzeczywistego korzystania z aplikacji i unikanie testowania szczegółów implementacyjnych. RTL oferuje mnóstwo narzędzi (utils), które upraszczają testowanie i ułatwiają utrzymanie testów.
Rozważmy prosty komponent React. Składa się z etykiety połączonej z polem input, przycisku wyświetlającego wiadomość oraz akapitu do pokazania tej wiadomości. Etykieta jest powiązana z polem input przez właściwość „htmlFor”, a przycisk ma obsługę zdarzenia onClick, które zmienia stan wiadomości.
Ten prosty komponent zawiera więc etykietę połączoną z inputem, przycisk do pokazania komunikatu i akapit, w którym go wyświetlimy.
Dodajmy do niego test: najpierw renderujemy komponent, wybieramy elementy, z którymi chcemy wchodzić w interakcję lub je sprawdzać, symulujemy wpisywanie i kliknięcie, a na końcu weryfikujemy, czy spodziewany komunikat pojawił się w akapicie. Taki test mógłby wyglądać tak:
test('user interaction test', () => {
const { getByLabelText, getByText } = render(<MyComponent />);
const input = getByLabelText('message-input');
const button = getByText('Show Message');
const messageContainer = getByText('');
expect(messageContainer.textContent).toBe('');
fireEvent.change(input, { target: { value: 'Hello World' } });
fireEvent.click(button);
expect(messageContainer.textContent).toBe('Hello World');
});Przejdźmy przez ten przykład:
1. Importujemy kilka helperów dostarczanych przez RTL oraz testowany komponent.
2. Tworzymy nowy test i nadajemy mu nazwę.
3. Używamy metody render z RTL, aby wyrenderować komponent. Metoda zwraca tzw. „queries”, które pozwalają wybierać elementy potrzebne w teście.
4. Używamy dostarczonych queries, aby wybrać input, przycisk i akapit, w którym wyświetlana jest wiadomość. Możemy wybierać elementy na różne sposoby, także gdy pojawiają się asynchronicznie, i korzystać z wyrażeń regularnych (regexp), np. bez rozróżniania wielkości liter. Najciekawsze jest „getByLabelText”, bo pozwala wybrać input, wychodząc od powiązanej z nim etykiety. Możemy też sprawdzić, czy nasze pola są dostępne.
5. Na początku nie powinno tam być żadnej wiadomości, więc to testujemy.
6. Używamy helpera do zasymulowania wpisywania przez użytkownika.
7. Symulujemy kolejne zdarzenie: kliknięcie w przycisk.
8. Dodajemy kolejne oczekiwanie, aby sprawdzić, czy kontener wiadomości zawiera przekazaną wartość.
Miejmy nadzieję, że test przejdzie!
Testy end-to-end (E2E) frontendu
Ostatnia część Testing Trophy to testy end-to-end. Różnią się od innych rodzajów testów tym, że uruchamiane są w prawdziwej przeglądarce. Piszemy przypadki testowe jako instrukcje krok po kroku dla zautomatyzowanej przeglądarki, aby przeszła przez te części aplikacji, które chcemy sprawdzić.
Jednym z najpopularniejszych narzędzi do testów end-to-end jest Cypress. Jest łatwy do skonfigurowania i użycia, a do tego szybki.
Oto przykład testu end-to-end formularza rejestracji w Cypress:
it('tests user registration', () => {
cy.visit('https://mywebsite.com/register');
cy.get('input[name="username"]').type('testUser');
cy.get('input[name="password"]').type('password123');
cy.get('button').contains('Register').click();
cy.url().should('include', '/login');
});W tym teście przechodzimy na stronę rejestracji, wypełniamy pola nazwy użytkownika i hasła, klikamy przycisk rejestracji, a następnie sprawdzamy, czy nastąpiło przekierowanie na stronę logowania.
1. Tworzymy test.
2. Polecamy Cypressowi odwiedzić adres z formularzem rejestracji.
3. Pobieramy odpowiednie inputy i wpisujemy wartości.
4. Szukamy przycisku z tekstem „Register” i klikamy go.
5. Po udanej rejestracji powinniśmy zostać przekierowani na stronę „/login”, więc to weryfikujemy.
Po uruchomieniu testu pojawia się okno przeglądarki, więc możemy zobaczyć, jak runner testów wykonuje kolejne kroki. Jeśli coś pójdzie nie tak, Cypress nas o tym poinformuje.
Zazwyczaj testy end-to-end pisze się dłużej niż jednostkowe czy integracyjne. Dłużej też się uruchamiają, bo komunikują się z prawdziwym API, dlatego warto pisać je tylko dla najważniejszych ścieżek w aplikacji.
Na koniec o testowaniu frontendu
Testing Trophy to świetna wskazówka. Zaczynamy od testów statycznych, potem przechodzimy do jednostkowych, a następnie integracyjnych. Moc testów end-to-end wykorzystujemy do automatyzacji najbardziej krytycznych ścieżek w aplikacji. Napisz do nas, aby dowiedzieć się więcej o testowaniu frontendu lub na adres hello@start-up.house
Digital Transformation Strategy for Siemens Finance
Cloud-based platform for Siemens Financial Services in Poland


Może Ci się również spodobać...

Wycieki pamięci w C++: przyczyny, narzędzia i jak im zapobiegać?
Poruszanie się po zawiłościach wycieków pamięci w C++ właśnie stało się prostsze. Nasz wyczerpujący przewodnik przedstawia narzędzia do wykrywania i techniki zapobiegania, pomagając zwiększyć wydajność systemu i uniknąć potencjalnych problemów. Zajrzyj do naszej sekcji FAQ, aby znaleźć dogłębne omówienie najczęstszych pytań dotyczących wycieków pamięci w C++.
Marek Majdak
19 wrz 2023・5 min czytania

5 prostych kroków do skutecznego Bug Basha
Sesje bug bash stały się popularną praktyką w zespołach deweloperskich, pomagając usprawnić wykrywanie błędów i podnieść jakość produktu. W tym artykule wyjaśniamy, czym są sesje bug bash, jakie przynoszą korzyści i kiedy warto z nich korzystać. Znajdziesz tu szczegółowe wskazówki, jak przygotować i przeprowadzić udaną sesję bug bash: od zdefiniowania ról, określenia zakresu testów i przygotowania szablonów zgłoszeń błędów, po poprowadzenie samego wydarzenia bug bash. Pokażemy też korzyści wykraczające poza samo wykrywanie błędów, takie jak wspieranie współpracy zespołowej i pogłębianie zrozumienia cyklu rozwoju produktu.
Valeriia Oliinyk
02 cze 2020・6 min czytania

Czym są przypadki brzegowe w tworzeniu i testowaniu oprogramowania?
Przypadki brzegowe odgrywają kluczową rolę w tworzeniu oprogramowania, często decydując o jego niezawodności i doświadczeniu użytkownika (UX). Zrozumienie, ustalanie priorytetów i skuteczne testowanie tych nietypowych scenariuszy pozwala programistom zapewnić większą robustność produktu. Ten kompleksowy przewodnik wyjaśnia, jak istotne są przypadki brzegowe i jak umiejętnie sobie z nimi radzić.
Marek Majdak
13 cze 2022・5 min czytania
Gotowy, aby scentralizować swoje know-how z pomocą AI?
Rozpocznij nowy rozdział w zarządzaniu wiedzą — gdzie Asystent AI staje się centralnym filarem Twojego cyfrowego wsparcia.
Umów bezpłatną konsultacjęPracuj z zespołem, któremu ufają firmy z czołówki rynku.




