BemÀstra React-komponenttestning med React Testing Library. LÀr dig bÀsta praxis för att skriva underhÄllbara, effektiva tester som fokuserar pÄ anvÀndarbeteende och tillgÀnglighet.
React Testing Library: BÀsta praxis för komponenttestning för globala team
I den stÀndigt förÀnderliga vÀrlden av webbutveckling Àr det av yttersta vikt att sÀkerstÀlla tillförlitligheten och kvaliteten pÄ dina React-applikationer. Detta gÀller sÀrskilt för globala team som arbetar med projekt med olika anvÀndarbaser och tillgÀnglighetskrav. React Testing Library (RTL) erbjuder en kraftfull och anvÀndarcentrerad metod för komponenttestning. Till skillnad frÄn traditionella testmetoder som fokuserar pÄ implementeringsdetaljer, uppmuntrar RTL dig att testa dina komponenter som en anvÀndare skulle interagera med dem, vilket leder till mer robusta och underhÄllbara tester. Denna omfattande guide kommer att fördjupa sig i bÀsta praxis för att anvÀnda RTL i dina React-projekt, med fokus pÄ att bygga applikationer som Àr lÀmpliga för en global publik.
Varför React Testing Library?
Innan vi dyker in i bÀsta praxis Àr det avgörande att förstÄ varför RTL skiljer sig frÄn andra testbibliotek. HÀr Àr nÄgra viktiga fördelar:
- AnvÀndarcentrerat tillvÀgagÄngssÀtt: RTL prioriterar att testa komponenter ur anvÀndarens perspektiv. Du interagerar med komponenten med samma metoder som en anvÀndare skulle göra (t.ex. klicka pÄ knappar, skriva i inmatningsfÀlt), vilket sÀkerstÀller en mer realistisk och pÄlitlig testupplevelse.
- Fokus pÄ tillgÀnglighet: RTL frÀmjar skrivandet av tillgÀngliga komponenter genom att uppmuntra dig att testa dem pÄ ett sÀtt som tar hÀnsyn till anvÀndare med funktionsnedsÀttningar. Detta Àr i linje med globala tillgÀnglighetsstandarder som WCAG.
- Minskat underhÄll: Genom att undvika att testa implementeringsdetaljer (t.ex. internt state, specifika funktionsanrop) Àr det mindre troligt att RTL-tester gÄr sönder nÀr du refaktorerar din kod. Detta leder till mer underhÄllbara och motstÄndskraftiga tester.
- FörbÀttrad koddesign: Det anvÀndarcentrerade tillvÀgagÄngssÀttet i RTL leder ofta till bÀttre komponentdesign, eftersom du tvingas tÀnka pÄ hur anvÀndare kommer att interagera med dina komponenter.
- Community och ekosystem: RTL har en stor och aktiv community som erbjuder rikligt med resurser, support och tillÀgg.
Konfigurera din testmiljö
För att komma igÄng med RTL mÄste du konfigurera din testmiljö. HÀr Àr en grundlÀggande konfiguration med Create React App (CRA), som levereras med Jest och RTL förkonfigurerat:
npx create-react-app my-react-app
cd my-react-app
npm install --save-dev @testing-library/react @testing-library/jest-dom
Förklaring:
- `npx create-react-app my-react-app`: Skapar ett nytt React-projekt med Create React App.
- `cd my-react-app`: Navigerar in i den nyskapade projektkatalogen.
- `npm install --save-dev @testing-library/react @testing-library/jest-dom`: Installerar de nödvÀndiga RTL-paketen som utvecklingsberoenden. `@testing-library/react` tillhandahÄller den grundlÀggande RTL-funktionaliteten, medan `@testing-library/jest-dom` erbjuder hjÀlpsamma Jest-matchers för att arbeta med DOM.
Om du inte anvÀnder CRA mÄste du installera Jest och RTL separat och konfigurera Jest för att anvÀnda RTL.
BÀsta praxis för komponenttestning med React Testing Library
1. Skriv tester som efterliknar anvÀndarinteraktioner
Grundprincipen för RTL Àr att testa komponenter som en anvÀndare skulle göra. Det innebÀr att fokusera pÄ vad anvÀndaren ser och gör, snarare Àn interna implementeringsdetaljer. AnvÀnd `screen`-objektet som tillhandahÄlls av RTL för att söka efter element baserat pÄ deras text, roll eller tillgÀnglighetsetiketter.
Exempel: Testa ett knapptryck
LÄt oss sÀga att du har en enkel knappkomponent:
// Button.js
import React from 'react';
function Button({ onClick, children }) {
return ;
}
export default Button;
SÄ hÀr skulle du testa den med 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);
});
});
Förklaring:
- `render()`: Renderar Button-komponenten med en mockad `onClick`-hanterare.
- `screen.getByText('Click Me')`: Söker i dokumentet efter ett element som innehÄller texten "Click Me". Det Àr sÄ en anvÀndare skulle identifiera knappen.
- `fireEvent.click(buttonElement)`: Simulerar ett klickevenemang pÄ knappelementet.
- `expect(handleClick).toHaveBeenCalledTimes(1)`: BekrÀftar att `onClick`-hanteraren anropades en gÄng.
Varför detta Àr bÀttre Àn att testa implementeringsdetaljer: FörestÀll dig att du refaktorerar Button-komponenten för att anvÀnda en annan hÀndelsehanterare eller Àndrar det interna tillstÄndet. Om du testade den specifika hÀndelsehanteringsfunktionen skulle ditt test gÄ sönder. Genom att fokusera pÄ anvÀndarinteraktionen (att klicka pÄ knappen) förblir testet giltigt Àven efter refaktorering.
2. Prioritera sökfrÄgor baserat pÄ anvÀndarens avsikt
RTL tillhandahÄller olika sökmetoder för att hitta element. Prioritera följande sökfrÄgor i denna ordning, eftersom de bÀst Äterspeglar hur anvÀndare uppfattar och interagerar med dina komponenter:
- getByRole: Denna sökfrÄga Àr den mest tillgÀngliga och bör vara ditt förstahandsval. Den lÄter dig hitta element baserat pÄ deras ARIA-roller (t.ex. button, link, heading).
- getByLabelText: AnvÀnd detta för att hitta element som Àr associerade med en specifik etikett, sÄsom inmatningsfÀlt.
- getByPlaceholderText: AnvÀnd detta för att hitta inmatningsfÀlt baserat pÄ deras platshÄllartext.
- getByText: AnvÀnd detta för att hitta element baserat pÄ deras textinnehÄll. Var specifik och undvik att anvÀnda generisk text som kan förekomma pÄ flera stÀllen.
- getByDisplayValue: AnvÀnd detta för att hitta inmatningsfÀlt baserat pÄ deras nuvarande vÀrde.
Exempel: Testa ett formulÀrfÀlt
// Input.js
import React from 'react';
function Input({ label, placeholder, value, onChange }) {
return (
);
}
export default Input;
SÄ hÀr testar du det med den rekommenderade sökordningen:
// 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' } }));
});
});
Förklaring:
- `screen.getByLabelText('Name')`: AnvÀnder `getByLabelText` för att hitta inmatningsfÀltet som Àr associerat med etiketten "Name". Detta Àr det mest tillgÀngliga och anvÀndarvÀnliga sÀttet att hitta fÀltet.
3. Undvik att testa implementeringsdetaljer
Som nÀmnts tidigare, undvik att testa internt state, funktionsanrop eller specifika CSS-klasser. Dessa Àr implementeringsdetaljer som kan komma att Àndras och kan leda till brÀckliga tester. Fokusera pÄ komponentens observerbara beteende.
Exempel: Undvik att testa state direkt
IstÀllet för att testa om en specifik state-variabel uppdateras, testa om komponenten renderar rÀtt utdata baserat pÄ det tillstÄndet. Till exempel, om en komponent visar ett meddelande baserat pÄ en boolesk state-variabel, testa om meddelandet visas eller döljs, snarare Àn att testa sjÀlva state-variabeln.
4. AnvÀnd data-testid i specifika fall
Ăven om det generellt sett Ă€r bĂ€st att undvika att anvĂ€nda `data-testid`-attribut, finns det specifika fall dĂ€r de kan vara anvĂ€ndbara:
- Element utan semantisk betydelse: Om du behöver rikta in dig pÄ ett element som inte har en meningsfull roll, etikett eller text, kan du anvÀnda `data-testid`.
- Komplexa komponentstrukturer: I komplexa komponentstrukturer kan `data-testid` hjÀlpa dig att rikta in dig pÄ specifika element utan att förlita dig pÄ brÀckliga selektorer.
- TillgÀnglighetstestning: `data-testid` kan anvÀndas för att identifiera specifika element under tillgÀnglighetstestning med verktyg som Cypress eller Playwright.
Exempel: AnvÀnda 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();
});
});
Viktigt: AnvÀnd `data-testid` sparsamt och endast nÀr andra sökmetoder inte Àr lÀmpliga.
5. Skriv meningsfulla testbeskrivningar
Tydliga och koncisa testbeskrivningar Àr avgörande för att förstÄ syftet med varje test och för att felsöka fel. AnvÀnd beskrivande namn som tydligt förklarar vad testet verifierar.
Exempel: Bra vs. dÄliga testbeskrivningar
DÄlig: `it('works')`
Bra: `it('displays the correct greeting message')`
Ănnu bĂ€ttre: `it('displays the greeting message "Hello, World!" when the name prop is not provided')`
Det bÀttre exemplet anger tydligt det förvÀntade beteendet hos komponenten under specifika förhÄllanden.
6. HÄll dina tester smÄ och fokuserade
Varje test bör fokusera pÄ att verifiera en enda aspekt av komponentens beteende. Undvik att skriva stora, komplexa tester som tÀcker flera scenarier. SmÄ, fokuserade tester Àr lÀttare att förstÄ, underhÄlla och felsöka.
7. AnvÀnd test-doubles (mocks och spies) pÄ rÀtt sÀtt
Test-doubles Àr anvÀndbara för att isolera komponenten du testar frÄn dess beroenden. AnvÀnd mocks och spies för att simulera externa tjÀnster, API-anrop eller andra komponenter.
Exempel: Mocka ett API-anrop
// 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();
});
});
Förklaring:
- `global.fetch = jest.fn(...)`: Mockar `fetch`-funktionen för att returnera en fördefinierad lista med anvÀndare. Detta gör att du kan testa komponenten utan att vara beroende av en riktig API-slutpunkt.
- `await waitFor(() => screen.getByText('John Doe'))`: VÀntar pÄ att texten "John Doe" ska dyka upp i dokumentet. Detta Àr nödvÀndigt eftersom datan hÀmtas asynkront.
8. Testa grÀnsfall och felhantering
Testa inte bara det lyckade fallet ("happy path"). Se till att testa grÀnsfall, felscenarier och randvillkor. Detta hjÀlper dig att identifiera potentiella problem tidigt och sÀkerstÀlla att din komponent hanterar ovÀntade situationer pÄ ett elegant sÀtt.
Exempel: Testa felhantering
FörestÀll dig en komponent som hÀmtar data frÄn ett API och visar ett felmeddelande om API-anropet misslyckas. Du bör skriva ett test för att verifiera att felmeddelandet visas korrekt nÀr API-anropet misslyckas.
9. Fokusera pÄ tillgÀnglighet
TillgÀnglighet Àr avgörande för att skapa inkluderande webbapplikationer. AnvÀnd RTL för att testa tillgÀngligheten hos dina komponenter och sÀkerstÀlla att de uppfyller tillgÀnglighetsstandarder som WCAG. NÄgra viktiga tillgÀnglighetsaspekter inkluderar:
- Semantisk HTML: AnvÀnd semantiska HTML-element (t.ex. `
- ARIA-attribut: AnvÀnd ARIA-attribut för att ge ytterligare information om roll, tillstÄnd och egenskaper hos element, sÀrskilt för anpassade komponenter.
- Tangentbordsnavigering: Se till att alla interaktiva element Àr Ätkomliga via tangentbordsnavigering.
- FÀrgkontrast: AnvÀnd tillrÀcklig fÀrgkontrast för att sÀkerstÀlla att texten Àr lÀsbar för anvÀndare med nedsatt syn.
- SkÀrmlÀsarkompatibilitet: Testa dina komponenter med en skÀrmlÀsare för att sÀkerstÀlla att de ger en meningsfull och begriplig upplevelse för anvÀndare med synnedsÀttningar.
Exempel: Testa tillgÀnglighet med 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();
});
});
Förklaring:
- `screen.getByRole('button', { name: 'Close' })`: AnvÀnder `getByRole` för att hitta ett knappelement med det tillgÀngliga namnet "Close". Detta sÀkerstÀller att knappen Àr korrekt mÀrkt för skÀrmlÀsare.
10. Integrera testning i ditt utvecklingsflöde
Testning bör vara en integrerad del av ditt utvecklingsflöde, inte en eftertanke. Integrera dina tester i din CI/CD-pipeline för att automatiskt köra tester nÀr kod checkas in eller distribueras. Detta hjÀlper dig att fÄnga buggar tidigt och förhindra regressioner.
11. TÀnk pÄ lokalisering och internationalisering (i18n)
För globala applikationer Àr det avgörande att ta hÀnsyn till lokalisering och internationalisering (i18n) under testningen. Se till att dina komponenter renderas korrekt pÄ olika sprÄk och i olika regioner.
Exempel: Testa lokalisering
Om du anvÀnder ett bibliotek som `react-intl` eller `i18next` för lokalisering, kan du mocka lokaliseringskontexten i dina tester för att verifiera att dina komponenter visar rÀtt översatt text.
12. AnvÀnd anpassade render-funktioner för ÄteranvÀndbar konfiguration
NÀr du arbetar med större projekt kan du upptÀcka att du upprepar samma konfigurationssteg i flera tester. För att undvika duplicering, skapa anpassade render-funktioner som kapslar in den gemensamma konfigurationslogiken.
Exempel: Anpassad render-funktion
// 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 })
// Äterexportera allt
export * from '@testing-library/react'
// ÄsidosÀtt render-metoden
export { customRender as render }
// MyComponent.test.js
import React from 'react';
import { render, screen } from './test-utils'; // Importera den anpassade render-funktionen
import MyComponent from './MyComponent';
describe('MyComponent', () => {
it('renders correctly with the theme', () => {
render( );
// Din testlogik hÀr
});
});
Detta exempel skapar en anpassad render-funktion som omsluter komponenten med en ThemeProvider. Detta gör att du enkelt kan testa komponenter som Àr beroende av temat utan att behöva upprepa ThemeProvider-konfigurationen i varje test.
Sammanfattning
React Testing Library erbjuder en kraftfull och anvÀndarcentrerad metod för komponenttestning. Genom att följa dessa bÀsta praxis kan du skriva underhÄllbara, effektiva tester som fokuserar pÄ anvÀndarbeteende och tillgÀnglighet. Detta kommer att leda till mer robusta, pÄlitliga och inkluderande React-applikationer för en global publik. Kom ihÄg att prioritera anvÀndarinteraktioner, undvika att testa implementeringsdetaljer, fokusera pÄ tillgÀnglighet och integrera testning i ditt utvecklingsflöde. Genom att anamma dessa principer kan du bygga högkvalitativa React-applikationer som möter behoven hos anvÀndare över hela vÀrlden.
Viktiga lÀrdomar:
- Fokusera pÄ anvÀndarinteraktioner: Testa komponenter som en anvÀndare skulle interagera med dem.
- Prioritera tillgÀnglighet: Se till att dina komponenter Àr tillgÀngliga för anvÀndare med funktionsnedsÀttningar.
- Undvik implementeringsdetaljer: Testa inte internt state eller funktionsanrop.
- Skriv tydliga och koncisa tester: Gör dina tester lÀtta att förstÄ och underhÄlla.
- Integrera testning i ditt arbetsflöde: Automatisera dina tester och kör dem regelbundet.
- TÀnk pÄ en global publik: Se till att dina komponenter fungerar bra pÄ olika sprÄk och i olika regioner.