Case StudiesBlogO nas
Porozmawiajmy

Testowanie frontendu: testy statyczne vs jednostkowe vs integracyjne vs E2E

Mateusz Wójcik

20 lip 20205 min czytania

Software testing

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

Opublikowany 20 lipca 2020

Udostępnij


Mateusz Wójcik

JavaScript Developer

Digital Transformation Strategy for Siemens Finance

Cloud-based platform for Siemens Financial Services in Poland

See full Case Study
Ad image
Testowanie frontendu: testy statyczne vs jednostkowe vs integracyjne vs E2E
Nie przegap żadnego artykułu - zapisz się do naszego newslettera
Zgadzam się na otrzymywanie komunikacji marketingowej od Startup House. Kliknij, aby zobaczyć szczegóły

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

Ruby on Rails - guide
Software developmentSoftware testing

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 20235 min czytania

5 prostych kroków do skutecznego Bug Basha
Software testing

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 20206 min czytania

Czym są przypadki brzegowe w tworzeniu i testowaniu oprogramowania?
Software developmentSoftware testing

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 20225 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.

Rainbow logo
Siemens logo
Toyota logo

Budujemy to, co będzie dalej.

Firma

Startup Development House sp. z o.o.

Aleje Jerozolimskie 81

Warszawa, 02-001

VAT-ID: PL5213739631

KRS: 0000624654

REGON: 364787848

Kontakt

hello@startup-house.com

Nasze biuro: +48 789 011 336

Nowy biznes: +48 798 874 852

Obserwuj nas

Award
logologologologo

Copyright © 2026 Startup Development House sp. z o.o.

UE ProjektyPolityka prywatności