Reactjs Testing Patterns React Testing Library User Event Mocking Network Calls Test Resilience
Definisi
Testing komponen React dengan React Testing Library (RTL) berfokus pada perilaku yang dilihat user, bukan detail implementasi. RTL menyediakan query berbasis role, label, dan teks yang stabil terhadap perubahan class atau struktur DOM.
Kapan dipakai
Uji komponen yang punya logika non-trivial: form dengan validasi, interaksi async, state bersyarat, atau komponen yang memanggil API. Komponen murni presentasional sederhana biasanya tidak butuh test sendiri.
Cara kerja
Setup dasar
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LoginForm } from './LoginForm';
test('submit mengirim email dan password', async () => {
const onSubmit = jest.fn();
render(<LoginForm onSubmit={onSubmit} />);
await userEvent.type(screen.getByLabelText(/email/i), 'a@b.com');
await userEvent.type(screen.getByLabelText(/password/i), 'rahasia');
await userEvent.click(screen.getByRole('button', { name: /masuk/i }));
expect(onSubmit).toHaveBeenCalledWith({
email: 'a@b.com',
password: 'rahasia',
});
});Mock network call
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('/api/user', (req, res, ctx) =>
res(ctx.json({ name: 'Budi' }))
)
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());Alur test
Loading diagram...
Contoh等待 async
test('menampilkan data setelah fetch', async () => {
render(<Profile />);
expect(await screen.findByText(/Budi/i)).toBeInTheDocument();
});findBy* otomatis menunggu elemen muncul, cocok untuk respons async.
Catatan implementasi
- Pilih query dari yang paling dekat dengan user:
getByRole>getByLabelText>getByText>getByTestId. HindarigetByTestIdkalau bisa pakai yang lain. - Pakai
userEventbukanfireEventuntuk interaksi yang meniru nyata (termasuk delay dan event urutan). - Mock di batas jaringan (MSW), bukan di dalam komponen, supaya test meniru perilaku asli.
Kesalahan umum
- Test rapuh karena class/struktur: query pakai
container.querySelector('.btn-primary')ikut rusak kalau class diubah, padahal behavior tetap. - Lupa
awaituserEvent: event async tidak selesai sebelum assert, hasil flaky. - Over-mock: mock terlalu dalam sampai test tidak lagi menguji integrasi nyata.
- Assert pada state internal: mengecek variabel closure bukannya output yang terlihat user.