Ovládněte testování komponent v Reactu s React Testing Library. Naučte se osvědčené postupy pro psaní udržitelných a efektivních testů zaměřených na chování uživatelů a přístupnost.
React Testing Library: Osvědčené postupy pro testování komponent pro globální týmy
V neustále se vyvíjejícím světě webového vývoje je zajištění spolehlivosti a kvality vašich React aplikací prvořadé. To platí zejména pro globální týmy pracující na projektech s různorodou uživatelskou základnou a požadavky na přístupnost. React Testing Library (RTL) poskytuje silný a na uživatele zaměřený přístup k testování komponent. Na rozdíl od tradičních testovacích metod, které se zaměřují na detaily implementace, RTL vás povzbuzuje k testování vašich komponent tak, jak by s nimi interagoval uživatel, což vede k robustnějším a udržitelnějším testům. Tento komplexní průvodce se ponoří do osvědčených postupů pro využití RTL ve vašich React projektech, s důrazem na budování aplikací vhodných pro globální publikum.
Proč React Testing Library?
Než se ponoříme do osvědčených postupů, je klíčové pochopit, proč RTL vyniká nad ostatními testovacími knihovnami. Zde jsou některé klíčové výhody:
- Přístup zaměřený na uživatele: RTL upřednostňuje testování komponent z pohledu uživatele. S komponentou interagujete pomocí stejných metod, jaké by použil uživatel (např. klikání na tlačítka, psaní do vstupních polí), což zajišťuje realističtější a spolehlivější testování.
- Zaměření na přístupnost: RTL podporuje psaní přístupných komponent tím, že vás vybízí k jejich testování způsobem, který zohledňuje uživatele s postižením. To je v souladu s globálními standardy přístupnosti, jako je WCAG.
- Snížená údržba: Díky tomu, že se vyhýbáte testování implementačních detailů (např. interního stavu, volání konkrétních funkcí), je méně pravděpodobné, že se testy RTL rozbijí při refaktorování vašeho kódu. To vede k udržitelnějším a odolnějším testům.
- Vylepšený návrh kódu: Přístup RTL zaměřený na uživatele často vede k lepšímu návrhu komponent, protože jste nuceni přemýšlet o tom, jak budou uživatelé s vašimi komponentami interagovat.
- Komunita a ekosystém: RTL se může pochlubit velkou a aktivní komunitou, která poskytuje dostatek zdrojů, podpory a rozšíření.
Nastavení vašeho testovacího prostředí
Abyste mohli začít s RTL, musíte si nastavit své testovací prostředí. Zde je základní nastavení pomocí Create React App (CRA), které je dodáváno s předkonfigurovaným Jest a RTL:
npx create-react-app my-react-app
cd my-react-app
npm install --save-dev @testing-library/react @testing-library/jest-dom
Vysvětlení:
- `npx create-react-app my-react-app`: Vytvoří nový React projekt pomocí Create React App.
- `cd my-react-app`: Přejde do nově vytvořeného adresáře projektu.
- `npm install --save-dev @testing-library/react @testing-library/jest-dom`: Nainstaluje potřebné RTL balíčky jako vývojové závislosti. `@testing-library/react` poskytuje základní funkcionalitu RTL, zatímco `@testing-library/jest-dom` poskytuje užitečné Jest matchery pro práci s DOM.
Pokud nepoužíváte CRA, budete muset nainstalovat Jest a RTL samostatně a nakonfigurovat Jest pro použití s RTL.
Osvědčené postupy pro testování komponent s React Testing Library
1. Pište testy, které napodobují interakce uživatele
Základním principem RTL je testovat komponenty tak, jak by to dělal uživatel. To znamená zaměřit se na to, co uživatel vidí a dělá, spíše než na interní detaily implementace. Použijte objekt `screen` poskytovaný RTL k dotazování na prvky na základě jejich textu, role nebo popisků pro přístupnost.
Příklad: Testování kliknutí na tlačítko
Řekněme, že máte jednoduchou komponentu tlačítka:
// Button.js
import React from 'react';
function Button({ onClick, children }) {
return ;
}
export default Button;
Zde je, jak byste ji testovali pomocí RTL:
// Button.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
describe('Button Component', () => {
it('calls the onClick handler when clicked', () => {
const handleClick = jest.fn();
render();
const buttonElement = screen.getByText('Click Me');
fireEvent.click(buttonElement);
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
Vysvětlení:
- `render()`: Vykreslí komponentu Button s mockovaným handlerem `onClick`.
- `screen.getByText('Click Me')`: Dotáže se v dokumentu na prvek, který obsahuje text „Click Me“. Takto by uživatel identifikoval tlačítko.
- `fireEvent.click(buttonElement)`: Simuluje událost kliknutí na prvku tlačítka.
- `expect(handleClick).toHaveBeenCalledTimes(1)`: Ověří, že handler `onClick` byl zavolán jednou.
Proč je to lepší než testování implementačních detailů: Představte si, že refaktorujete komponentu Button, aby používala jiný handler událostí nebo změnila interní stav. Kdybyste testovali konkrétní funkci handleru událostí, váš test by se rozbil. Tím, že se zaměříte na interakci uživatele (kliknutí na tlačítko), test zůstává platný i po refaktorování.
2. Upřednostňujte dotazy na základě záměru uživatele
RTL poskytuje různé metody dotazování pro nalezení prvků. Upřednostňujte následující dotazy v tomto pořadí, protože nejlépe odrážejí, jak uživatelé vnímají a interagují s vašimi komponentami:
- getByRole: Tento dotaz je nejpřístupnější a měl by být vaší první volbou. Umožňuje vám najít prvky na základě jejich ARIA rolí (např. button, link, heading).
- getByLabelText: Použijte pro nalezení prvků spojených s konkrétním popiskem, jako jsou vstupní pole.
- getByPlaceholderText: Použijte pro nalezení vstupních polí na základě jejich zástupného textu (placeholder).
- getByText: Použijte pro nalezení prvků na základě jejich textového obsahu. Buďte konkrétní a vyhýbejte se obecnému textu, který by se mohl objevit na více místech.
- getByDisplayValue: Použijte pro nalezení vstupních polí na základě jejich aktuální hodnoty.
Příklad: Testování formulářového vstupu
// Input.js
import React from 'react';
function Input({ label, placeholder, value, onChange }) {
return (
);
}
export default Input;
Zde je, jak to testovat pomocí doporučeného pořadí dotazů:
// Input.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Input from './Input';
describe('Input Component', () => {
it('updates the value when the user types', () => {
const handleChange = jest.fn();
render();
const inputElement = screen.getByLabelText('Name');
fireEvent.change(inputElement, { target: { value: 'John Doe' } });
expect(handleChange).toHaveBeenCalledTimes(1);
expect(handleChange).toHaveBeenCalledWith(expect.objectContaining({ target: { value: 'John Doe' } }));
});
});
Vysvětlení:
- `screen.getByLabelText('Name')`: Používá `getByLabelText` k nalezení vstupního pole spojeného s popiskem „Name“. Toto je nejpřístupnější a uživatelsky nejpřívětivější způsob, jak lokalizovat vstup.
3. Vyhněte se testování implementačních detailů
Jak již bylo zmíněno, vyhněte se testování interního stavu, volání funkcí nebo konkrétních CSS tříd. Jsou to implementační detaily, které se mohou měnit a mohou vést k křehkým testům. Zaměřte se na pozorovatelné chování komponenty.
Příklad: Vyhněte se přímému testování stavu
Místo testování, zda je aktualizována konkrétní stavová proměnná, testujte, zda komponenta vykresluje správný výstup na základě tohoto stavu. Například, pokud komponenta zobrazuje zprávu na základě booleovské stavové proměnné, testujte, zda je zpráva zobrazena nebo skryta, nikoli samotnou stavovou proměnnou.
4. Používejte `data-testid` pro specifické případy
I když je obecně nejlepší se vyhnout používání atributů `data-testid`, existují specifické případy, kdy mohou být užitečné:
- Prvky bez sémantického významu: Pokud potřebujete zacílit prvek, který nemá smysluplnou roli, popisek nebo text, můžete použít `data-testid`.
- Složité struktury komponent: V složitých strukturách komponent vám `data-testid` může pomoci zacílit konkrétní prvky bez spoléhání na křehké selektory.
- Testování přístupnosti: `data-testid` lze použít k identifikaci konkrétních prvků během testování přístupnosti s nástroji jako Cypress nebo Playwright.
Příklad: Použití `data-testid`
// MyComponent.js
import React from 'react';
function MyComponent() {
return (
This is my component.
);
}
export default MyComponent;
// MyComponent.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
it('renders the component container', () => {
render( );
const containerElement = screen.getByTestId('my-component-container');
expect(containerElement).toBeInTheDocument();
});
});
Důležité: Používejte `data-testid` střídmě a pouze tehdy, když jiné metody dotazování nejsou vhodné.
5. Pište smysluplné popisky testů
Jasné a stručné popisky testů jsou klíčové pro pochopení účelu každého testu a pro ladění chyb. Používejte popisné názvy, které jasně vysvětlují, co test ověřuje.
Příklad: Dobré vs. špatné popisky testů
Špatný: `it('funguje')`
Dobrý: `it('zobrazí správnou uvítací zprávu')`
Ještě lepší: `it('zobrazí uvítací zprávu "Hello, World!", když není poskytnut prop name')`
Lepší příklad jasně uvádí očekávané chování komponenty za specifických podmínek.
6. Udržujte své testy malé a zaměřené
Každý test by se měl zaměřit na ověření jediného aspektu chování komponenty. Vyhněte se psaní velkých, složitých testů, které pokrývají více scénářů. Malé, zaměřené testy jsou snazší na pochopení, údržbu a ladění.
7. Používejte testovací dvojníky (mocky a spies) vhodně
Testovací dvojníci jsou užiteční pro izolaci komponenty, kterou testujete, od jejích závislostí. Používejte mocky a spies k simulaci externích služeb, volání API nebo jiných komponent.
Příklad: Mockování volání API
// UserList.js
import React, { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
async function fetchUsers() {
const response = await fetch('/api/users');
const data = await response.json();
setUsers(data);
}
fetchUsers();
}, []);
return (
{users.map(user => (
- {user.name}
))}
);
}
export default UserList;
// UserList.test.js
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import UserList from './UserList';
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve([
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' },
]),
})
);
describe('UserList Component', () => {
it('fetches and displays a list of users', async () => {
render( );
// Wait for the data to load
await waitFor(() => screen.getByText('John Doe'));
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('Jane Smith')).toBeInTheDocument();
});
});
Vysvětlení:
- `global.fetch = jest.fn(...)`: Mockuje funkci `fetch` tak, aby vracela předdefinovaný seznam uživatelů. To vám umožní testovat komponentu bez spoléhání na skutečný API endpoint.
- `await waitFor(() => screen.getByText('John Doe'))`: Čeká, dokud se v dokumentu neobjeví text „John Doe“. To je nutné, protože data jsou načítána asynchronně.
8. Testujte okrajové případy a zpracování chyb
Netestujte jen „šťastnou cestu“. Ujistěte se, že testujete okrajové případy, chybové scénáře a hraniční podmínky. To vám pomůže identifikovat potenciální problémy včas a zajistit, že vaše komponenta zvládá neočekávané situace elegantně.
Příklad: Testování zpracování chyb
Představte si komponentu, která načítá data z API a zobrazí chybovou zprávu, pokud volání API selže. Měli byste napsat test, který ověří, že chybová zpráva je správně zobrazena, když volání API selže.
9. Zaměřte se na přístupnost
Přístupnost je klíčová pro vytváření inkluzivních webových aplikací. Použijte RTL k testování přístupnosti vašich komponent a ujistěte se, že splňují standardy přístupnosti, jako je WCAG. Některé klíčové aspekty přístupnosti zahrnují:
- Sémantické HTML: Používejte sémantické HTML prvky (např., `
- ARIA atributy: Používejte ARIA atributy k poskytnutí dodatečných informací o roli, stavu a vlastnostech prvků, zejména u vlastních komponent.
- Klávesnicová navigace: Ujistěte se, že všechny interaktivní prvky jsou přístupné pomocí klávesnicové navigace.
- Barevný kontrast: Používejte dostatečný barevný kontrast, aby byl text čitelný pro uživatele se slabým zrakem.
- Kompatibilita se čtečkami obrazovky: Testujte své komponenty se čtečkou obrazovky, abyste zajistili, že poskytují smysluplný a srozumitelný zážitek pro uživatele se zrakovým postižením.
Příklad: Testování přístupnosti s `getByRole`
// MyAccessibleComponent.js
import React from 'react';
function MyAccessibleComponent() {
return (
);
}
export default MyAccessibleComponent;
// MyAccessibleComponent.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import MyAccessibleComponent from './MyAccessibleComponent';
describe('MyAccessibleComponent', () => {
it('renders an accessible button with the correct aria-label', () => {
render( );
const buttonElement = screen.getByRole('button', { name: 'Close' });
expect(buttonElement).toBeInTheDocument();
});
});
Vysvětlení:
- `screen.getByRole('button', { name: 'Close' })`: Používá `getByRole` k nalezení prvku tlačítka s přístupným názvem „Close“. Tím je zajištěno, že tlačítko je správně označeno pro čtečky obrazovky.
10. Integrujte testování do svého vývojového procesu
Testování by mělo být nedílnou součástí vašeho vývojového procesu, ne až dodatečnou myšlenkou. Integrujte své testy do vašeho CI/CD pipeline, aby se automaticky spouštěly při každém committu nebo nasazení kódu. To vám pomůže odhalit chyby včas a zabránit regresím.
11. Zvažte lokalizaci a internacionalizaci (i18n)
Pro globální aplikace je klíčové zvážit lokalizaci a internacionalizaci (i18n) během testování. Ujistěte se, že se vaše komponenty správně vykreslují v různých jazycích a lokalitách.
Příklad: Testování lokalizace
Pokud používáte knihovnu jako `react-intl` nebo `i18next` pro lokalizaci, můžete v testech mockovat lokalizační kontext, abyste ověřili, že vaše komponenty zobrazují správný přeložený text.
12. Používejte vlastní renderovací funkce pro znovupoužitelné nastavení
Při práci na větších projektech se vám může stát, že opakujete stejné kroky nastavení ve více testech. Abyste se vyhnuli duplicitě, vytvořte si vlastní renderovací funkce, které zapouzdří společnou logiku nastavení.
Příklad: Vlastní renderovací funkce
// test-utils.js
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeProvider } from 'styled-components';
import theme from './theme';
const AllTheProviders = ({ children }) => {
return (
{children}
);
}
const customRender = (ui, options) =>
render(ui, { wrapper: AllTheProviders, ...options })
// re-export everything
export * from '@testing-library/react'
// override render method
export { customRender as render }
// MyComponent.test.js
import React from 'react';
import { render, screen } from './test-utils'; // Import the custom render
import MyComponent from './MyComponent';
describe('MyComponent', () => {
it('renders correctly with the theme', () => {
render( );
// Your test logic here
});
});
Tento příklad vytváří vlastní renderovací funkci, která obaluje komponentu ThemeProviderem. To vám umožňuje snadno testovat komponenty, které závisí na tématu, aniž byste museli opakovat nastavení ThemeProvideru v každém testu.
Závěr
React Testing Library nabízí silný a na uživatele zaměřený přístup k testování komponent. Dodržováním těchto osvědčených postupů můžete psát udržovatelné a efektivní testy, které se zaměřují na chování uživatelů a přístupnost. To povede k robustnějším, spolehlivějším a inkluzivnějším React aplikacím pro globální publikum. Nezapomeňte upřednostňovat interakce uživatelů, vyhýbat se testování implementačních detailů, zaměřovat se na přístupnost a integrovat testování do svého vývojového procesu. Přijetím těchto principů můžete vytvářet vysoce kvalitní React aplikace, které splňují potřeby uživatelů po celém světě.
Klíčové poznatky:
- Zaměřte se na interakce uživatele: Testujte komponenty tak, jak by s nimi interagoval uživatel.
- Upřednostňujte přístupnost: Ujistěte se, že vaše komponenty jsou přístupné uživatelům s postižením.
- Vyhněte se implementačním detailům: Netestujte interní stav ani volání funkcí.
- Pište jasné a stručné testy: Učiňte své testy snadno srozumitelnými a udržovatelnými.
- Integrujte testování do svého pracovního postupu: Automatizujte své testy a spouštějte je pravidelně.
- Zvažte globální publikum: Ujistěte se, že vaše komponenty fungují dobře v různých jazycích a lokalitách.