Zpět na domů

React Testing Library: odmítnutí snímků

Článek popisuje přechod od Enzyme k React Testing Library v projektu. Analýza problémů se snímky a testy implementace. Check-listy pro psaní a revizi testů, doporučení pro meetupy a migraci.

RTL místo Enzyme: testy chování Reactu
Advertisement 728x90

Od snímků k chování: přechod na React Testing Library v testech

V typických projektech jsou testy často zaměřeny na základní strukturu, nikoli na funkčnost. Snímky (snapshots) zachycují stav DOM, ale selhávají při každém refaktoringu – změna tříd CSS, přesun atributů nebo optimalizace vykreslování. Skutečné chyby v logice zůstávají nezaznamenané.

Příklad problematického testu na Enzyme:

describe("UserProfileCards", () => {
    it("should render", () => {
        const wrapper = shallow(
            <Provider store={store}>
                <UserProfileCards items={mockedUserProfiles} />
            </Provider>
        )
        expect(wrapper).toMatchSnapshot()
    })
})

Tento test nedává jistotu o správném fungování komponenty. Zkontroluje strukturu, nikoli výsledek pro uživatele.

Google AdInline article slot

Přichycení k implementaci: falešné výpady

Testy, které se drží za interní detaily, vytvářejí dvě problémy: pád při refaktoru a přehlédnutí chyb.

Uvažujme komponentu CollapseIcon s příznakem opened:

import CollapseIcon from "./CollapseIcon"
import { shallow } from "enzyme"

describe("CollapseIcon", () => {
    it("should render", () => {
        const wrapper = shallow(<CollapseIcon opened={false} />)
        expect(wrapper).toMatchSnapshot()
    })

    it("should react to closed", () => {
        const wrapper = shallow(<CollapseIcon opened={false} />)
        expect(wrapper.find(".collapse__icon").hasClass("collapseIcon_closed")).toEqual(true)
    })

    it("should react to opened", () => {
        const wrapper = shallow(<CollapseIcon opened={true} />)
        expect(wrapper.find(".collapse__icon").hasClass("collapseIcon_opened")).toEqual(true)
    })
})

Problémy:

Google AdInline article slot
  • Snímek nezachytí chyby v animaci.
  • Ověření tříd selže při změně stylů.
  • Testy proběhnou i pokud je animace poškozená.

Další případ: pevná vazba na konstanty lokalizace:

it("should render no-answers button", () => {
    const button = getButton(mockCommentsList[0])
    const noAnswersText = `${mockedTranslation.t(`${tNamespace}no-answers`)}
    expect(button.text()).toBe(noAnswersText)
})

Změna klíče v tNamespace poruší test, i když text tlačítka je správný. Řešení: konstanty přesunout do společného modulu, který importuje jak komponenta, tak testy.

Nejasná popisování testů zhoršuje situaci:

Google AdInline article slot
  • "should show tooltip" – bez podmínek.
  • "should render without tooltipLabel" – bez očekávaného výsledku.

React Testing Library: testování chování

RTL mění paradigma: testy napodobují akce uživatele a ignorují interní detaily. Nemají přístup k state, props nebo metodám – pouze k viditelnému rozhraní.

Výhody RTL:

  • Fokus na uživatele: klikání, zadávání textu, čtení prostřednictvím screen.
  • Refaktoring-safe: nezávislé na třídách, id nebo interním stavu.
  • API: render(), screen.getByRole(), fireEvent.click(), waitFor().
  • Asynchronní operace: findByRole() pro načítání dat.
  • Přístupnost (a11y): getByLabelText() ověřuje dostupnost.

RTL je navržen pro odolnost: testy odrážejí skutečné používání, nikoli strukturu DOM.

Seznam kontrol pro kvalitní testy

Psaní testů

  • Cíl: jasně formulovat, co se testuje ("zobrazuje tooltip při isTooltipShow=true").
  • Pohled uživatele: akce jako uživatel – getByRole('button'), fireEvent.
  • Izolace: mocky pro API, žádné závislosti na jiných testech.
  • Popisy: úplné věty, čitelné bez kódu.
  • Pokrytí: happy path + hraniční případy + chyby.

Revize testů

  • Je cíl jasný?
  • Neobsahuje-li detaily implementace (třídy, stav)?
  • Je test izolovaný?
  • Jsou popisy informační?
  • Jsou komentáře konstruktivní?

Organizace přechodu ve týmu

Workshop na migraci: sbírat problematické testy, rozebírat na příkladech, přecházet na RTL. Doporučení:

  • Rozdělit na sesie po 45 minutách s účastí autorů.
  • Společný refaktoring testů ve pair-programmingu.
  • Méně teorie, více praxe na kódu.

Migrace legacy: nové testy na RTL, staré postupně přepsat. Příklad Airbnb – 3500 souborů za 6 týdnů s LLM-pipelinou (75 % automaticky).

Co je důležité

  • Změna myšlení: od struktury k chování uživatele.
  • RTL vs Enzyme: RTL blokuje přístup k interním částem, Enzyme jej povoluje.
  • Seznamy kontrol: povinné při psaní i revizi.
  • Workshopy: dialog je důležitější než monolog.
  • Migrace: automatizace urychlí, ale pochopení filozofie je nezbytné.

— Editorial Team

Advertisement 728x90

Číst dál