Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Snapshot Testing -- Zdjęcie Konstelacji

Snapshot testing to technika polegająca na "sfotografowaniu" wyniku renderowania komponentu i porównywaniu go z zapisanym wzorcem. To jak zrobienie zdjęcia konstelacji gwiazd -- jeśli przy następnym spojrzeniu jakieś gwiazdy się przesunęły, test to wykryje.

Jak działają snapshoty?

Mechanizm jest prosty. Przy pierwszym uruchomieniu testu, Jest renderuje komponent i zapisuje wynikowy HTML do specjalnego pliku

.snap
w folderze
__snapshots__
. Przy kolejnych uruchomieniach Jest ponownie renderuje komponent i porównuje wynik z zapisanym wzorcem. Jeśli coś się zmieniło -- test nie przechodzi. To jak porównywanie dwóch zdjęć konstelacji zrobionych w odstępie czasu -- jeśli gwiazdy się przesunęły, od razu to zauważysz.

Oto podstawowy przykład:

1import { render } from '@testing-library/react';
2import StatusBadge from './StatusBadge';
3
4test('renders active status badge', () => {
5  const { container } = render(<StatusBadge status="active" />);
6  expect(container.firstChild).toMatchSnapshot();
7});

Wygenerowany plik snapshot (.snap)

1// Jest Snapshot v1
2
3exports[`renders active status badge 1`] = `
4<span
5  class="badge badge-active"
6>
7  Active
8</span>
9`;

Inline Snapshots

Zamiast osobnego pliku, snapshot może być zapisany bezpośrednio w teście:

1test('renders fuel display', () => {
2  const { container } = render(<FuelDisplay level={75} />);
3  expect(container.firstChild).toMatchInlineSnapshot(`
4    <div
5      class="fuel-display"
6    >
7      <span>
8        Fuel: 75%
9      </span>
10    </div>
11  `);
12});

Aktualizowanie snapshotów

Gdy celowo zmieniamy komponent, snapshoty muszą być zaktualizowane:

1# Aktualizacja wszystkich snapshotów
2npm test -- --updateSnapshot
3
4# Lub skrótowo
5npm test -- -u

Kiedy używać snapshotów?

Dobre przypadki użycia

  • Komponenty prezentacyjne -- proste komponenty bez logiki
  • Wynikowe drzewo JSX -- sprawdzenie struktury HTML
  • Serializable data -- obiekty, tablice, JSON
1// Dobry snapshot -- prosty komponent
2test('renders planet card', () => {
3  const { container } = render(
4    <PlanetCard name="Mars" distance="225M km" color="red" />
5  );
6  expect(container.firstChild).toMatchSnapshot();
7});

Złe przypadki użycia

  • Komponenty z dynamiczną logiką -- snapshot nie testuje zachowania
  • Duże komponenty -- snapshoty stają się trudne do przeglądania
  • Komponenty z datami/losowymi ID -- snapshot zawsze się zmienia
1// ZŁY snapshot -- za duży, zbyt dynamiczny
2test('renders entire dashboard', () => {
3  const { container } = render(<Dashboard />); // 500 linii HTML!
4  expect(container).toMatchSnapshot(); // Nikt tego nie przeczyta!
5});

Snapshot vs. Targeted Assertions

To fundamentalne porównanie, które każdy programista React powinien rozumieć. Snapshot sprawdza całą strukturę komponentu -- każdy tag, klasę i atrybut. Targeted assertion sprawdza konkretne zachowanie -- czy tekst jest widoczny, czy przycisk jest aktywny, czy element ma odpowiednią klasę. Różnica jest jak między zrobieniem zdjęcia całego panelu sterowania a sprawdzeniem, czy konkretna kontrolka świeci na zielono.

1// Snapshot -- sprawdza CAŁĄ strukturę
2expect(container.firstChild).toMatchSnapshot();
3
4// Targeted assertion -- sprawdza KONKRETNE zachowanie (zalecane!)
5expect(screen.getByText('Active')).toHaveClass('badge-active');
6expect(screen.getByRole('button')).toBeEnabled();

Zasada: Preferuj targeted assertions nad snapshoty. Snapshoty łatwo stają się "niewidoczne" -- programiści często aktualizują je automatycznie komendą

-u
bez sprawdzania, co się zmieniło. Targeted assertions natomiast wymuszają świadome sprawdzenie konkretnego zachowania. Snapshoty są dobre jako dodatkowe zabezpieczenie przed nieoczekiwanymi zmianami wizualnymi, ale nie powinny być głównym sposobem testowania.

Snapshot serializers

Możemy dostosować jak snapshoty są formatowane:

1// W setupTests.js lub pliku konfiguracyjnym
2expect.addSnapshotSerializer({
3  test: (val) => val && val.className,
4  print: (val) => `ClassName: ${val.className}`,
5});

Custom snapshot matchers

1test('renders navigation with correct structure', () => {
2  const { container } = render(<SpaceNavigation routes={routes} />);
3
4  // Snapshot tylko części komponentu
5  expect(
6    container.querySelector('.nav-links')
7  ).toMatchSnapshot();
8});

Podsumowanie -- Kiedy snapshot, kiedy nie?

| Sytuacja | Snapshot | Targeted | |----------|----------|----------| | Prosty komponent UI | Tak | Tak | | Logika biznesowa | Nie | Tak | | Interakcje użytkownika | Nie | Tak | | Formatowanie danych | Tak | Tak | | Duży komponent | Nie | Tak | | Regresja wizualna | Tak | Nie |

Ir a CodeWorlds