Component tests i snapshot tests: mały fragment aplikacji
Unit test potrafi dobrze sprawdzić regułę, ale czasem błąd nie siedzi w jednej funkcji.
Formularz waliduje pole poprawnie, ale przycisk zostaje aktywny za wcześnie.
Komponent renderuje stan pusty, ale gubi komunikat błędu po drugim kliknięciu.
Mapper działa, ale po złożeniu z formatowaniem output ma inną strukturę niż zakłada reszta aplikacji.
To jest miejsce dla component test.
Co to za rodzaj testu
Component test sprawdza mały fragment aplikacji jako całość.
Jednostką może być:
komponent UI
formularz
widget
mały moduł domenowy
parser z publicznym API
adapter z lokalnym fake
Nie chodzi o pełny system.
Chodzi o fragment większy niż jedna funkcja, ale nadal wystarczająco mały, żeby diagnoza była szybka.
W frontendzie component test zwykle renderuje komponent z propsami, stanem, providerem albo prostym fake zależności.
W backendzie podobną rolę może pełnić test małego modułu przez jego publiczne API, bez prawdziwej bazy i bez uruchamiania serwera.
Czym różni się od unit testu
Unit test sprawdza decyzję w kodzie.
Component test sprawdza współpracę kilku decyzji w małym fragmencie.
unit test
rabat 100% nie daje ceny ujemnej
component test
formularz pokazuje cenę 0, blokuje płatność i pokazuje komunikat
Jeżeli unit test pada, zwykle wiadomo, która reguła jest zła.
Jeżeli component test pada, wiadomo, że fragment zachowania przestał działać, ale przyczyna może siedzieć w kilku miejscach.
To nadal jest tanie.
Nie trzeba odpalać całej aplikacji, logować użytkownika i przechodzić przez prawdziwą ścieżkę E2E.
Snapshot tests
Snapshot test zapisuje output i porównuje go z kolejnym uruchomieniem.
Outputem może być:
drzewo komponentu
HTML
JSON
tekstowy format danych
wygenerowany manifest
Snapshot nie mówi, że zachowanie jest poprawne.
Mówi:
output zmienił się względem zatwierdzonej wersji
To bywa użyteczne, kiedy output jest duży i łatwo przeoczyć zmianę w review.
Jest też zdradliwe.
Jeżeli zespół aktualizuje snapshoty komendą i nie czyta diffu, snapshot test staje się hałasem.
Co się dzieje, kiedy ich nie ma
Bez component tests drobne błędy sklejania uciekają do E2E.
E2E zaczyna sprawdzać, czy przycisk jest disabled, czy komunikat się pojawił, czy stan formularza został wyczyszczony.
To są realne zachowania, ale zbyt tanie, żeby sprawdzać je dopiero przez pełną ścieżkę systemu.
Bez snapshotów zmiana outputu może przejść niezauważona.
To ma znaczenie przy generowaniu schematów, manifestów, konfiguracji albo dużych fragmentów UI.
Ale snapshot nie zastępuje asercji.
Jeżeli najważniejsze jest to, że przycisk zapisuje zamówienie, lepsza jest asercja na zachowanie niż snapshot całego drzewa.
Narzędzie ze stacka
Do component tests w React wybrałbym React Testing Library z Vitest.
To ustawienie wymusza patrzenie na komponent przez zachowanie widoczne dla użytkownika: tekst, role, pola formularza, kliknięcia i komunikaty.
Do snapshotów użyłbym mechanizmu snapshotów z Vitest, ale oszczędnie.
Snapshot ma sens przy stabilnym outputcie, na przykład wygenerowanym HTML, JSON albo małym fragmencie komponentu.
Nie robiłbym snapshotu całej strony tylko po to, żeby mieć duży plik do akceptowania.
Kiedy są za duże
Component test zaczyna być za duży, kiedy musi udawać pół aplikacji.
kilka providerów
kilka mocków API
skomplikowany routing
fałszywy store
duża konfiguracja środowiska
Wtedy test traci prostotę unit testu, ale nadal nie daje pewności E2E.
To znak, że trzeba przesunąć granicę: część logiki zejść niżej do unit tests, a krytyczną ścieżkę zostawić wyżej.
Następny poziom to integration tests, czyli moment, w którym przestajemy udawać jedną z ważnych zależności.