React Testing Library: Jak pisać łatwe w utrzymaniu testy?
Opublikowano: 19.10.2023 - tagi: JavaScript React Testowanie Test
Czytelność testów
"Kod częściej się czyta, niż pisze", "Kod nie jest zapisany w skale". Te spostrzeżenia odnoszą się także do testów. W końcu testy to też kod!
W takim razie warto zastanowić się, jak można zwiększyć ich czytelność?
Nie tylko czytelność. Jak pisać testy łatwiejsze w utrzymaniu?
Lepsze testy — krok po kroku
Zacznijmy od przykładu.
Komponent:
export function MyComponent() {
const [state, setState] = useState(0);
return (
<>
<div>
{state}
</div>
<div>
<button onClick={() => setState((prevState: number) => prevState + 1)}>Increment</button>
<button onClick={() => setState((prevState: number) => prevState - 1)}>Decrement</button>
</div>
</>
)
}
Lepsze testy: Krok 1
const { getByRole, getByText } = screen;
test('should increment counter', async () => {
// given
render(<MyComponent />);
const btnIncrement = getByRole('button', { name: "Increment"});
// when
await userEvent.click(btnIncrement);
// then
expect(getByText('1')).toBeInTheDocument();
});
test('should decrement counter', async () => {
// given
render(<MyComponent />);
const btnDecrement = getByRole('button', { name: "Decrement"});
// when
await userEvent.click(btnDecrement);
// then
expect(getByText('-1')).toBeInTheDocument();
});
Co można zrobić, żeby test był bardziej czytelny?
Lepsze testy: Krok 2
Za każdym razem trzeba wywoływać funkcję: render(
Wrzućmy ją do funkcji: renderComponent:
const renderComponent = () => render(<MyComponent />);
Refaktoring testu:
test('should increment counter', async () => {
// given
renderComponent();
const btnIncrement = getByRole('button', { name: "Increment"});
...
});
test('should decrement counter', async () => {
// given
renderComponent();
const btnDecrement = getByRole('button', { name: "Decrement"});
...
});
Jest odrobinę lepiej.
Czas na następny krok.
Lepsze testy: Krok 3
Żeby testować element, potrzebujesz referencji do elementów znajdujących się w komponencie.
Pisanie zapytać w stylu:
const btnIncrement = getByRole('button', { name: "Increment"});
Jest męczące.
A co gdyby funkcja renderująca testowany komponent (renderComponent) zwracała referencje do elementów, które posiada?
Nowa wersja renderComponent:
const renderComponent = () => {
render(<MyComponent />);
const btnIncrement = getByRole('button', { name: "Increment"});
const btnDecrement = getByRole('button', { name: "Decrement"});
const userClicksOn = async (element) => await userEvent.click(element);
return {
btnIncrement,
btnDecrement,
userClicksOn
}
}
I zaktualizowane testy:
test('should increment counter', async () => {
// given
const { btnIncrement, userClicksOn } = renderComponent();
// when
await userClicksOn(btnIncrement);
// then
expect(getByText('1')).toBeInTheDocument();
});
test('should decrement counter', async () => {
// given
const { btnDecrement, userClicksOn } = renderComponent();
// when
await userClicksOn(btnDecrement);
// then
expect(getByText('-1')).toBeInTheDocument();
});
Podsumowanie
Takie podejście do pisania testów sprawia, że rozwiązujesz kilka problemów:
- Centralizacja — Ewentualne zmiany, będziesz robić w funkcji renderującej komponent. Nie musisz "skakać" po wielu testach, żeby coś naprawić.
- Testy są bardziej czytelne.