Testing, Debugging, dan Kualitas Kode · 3/7
Menguji component React
Cari berdasarkan role dan label, bukan nama class. Uji perilaku, bukan implementasi.
Baca 30 menit
Setelah pelajaran ini kamu bisa
- Mencari elemen dengan cara pengguna menemukannya
- Menguji perilaku alih-alih implementasi
- Menjelaskan kenapa `getByRole` adalah query yang diutamakan
- Menyebutkan apa yang tidak bisa diuji sebagai unit test di React
Aturan untuk test component, dari prinsip panduan Testing Library sendiri: temukan sesuatu dengan cara pengguna menemukannya. Pengguna tidak tahu nama class-mu. Mereka melihat sebuah button berlabel "Sign in".
import { render, screen } from "@testing-library/react";
import { expect, it } from "vitest";
import { Badge } from "./badge";
it("shows its label", () => {
render(<Badge tone="success">Paid</Badge>);
expect(screen.getByText("Paid")).toBeInTheDocument();
});
it("announces an icon-only button", () => {
render(<CloseButton />);
// The accessible name, not the markup.
expect(screen.getByRole("button", { name: "Close dialog" })).toBeInTheDocument();
});| Query | Dipakai saat | Prioritas |
|---|---|---|
getByRole(role, { name }) | Hampir selalu — button, link, heading, input | Pilihan pertama |
getByLabelText | Field form yang punya label | Bagus |
getByText | Teks non-interaktif | Boleh |
getByTestId | Tidak ada cara lain yang berhasil | Pilihan terakhir |
container.querySelector | Jangan pernah, di dalam test | Hindari |
Query berdasarkan accessible name, diimplementasikan di atas pohon elemen tiruan. Perhatikan elemen mana yang bisa dan tidak bisa ditemukannya.
Hasil
Tekan Jalankan untuk melihat hasilnya.
Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.
Perilaku, bukan implementasi
// ❌ Coupled to how it works. Renaming state breaks a passing test.
it("sets isOpen to true", () => {
const { result } = renderHook(() => useDisclosure());
act(() => result.current.open());
expect(result.current.isOpen).toBe(true);
});
// ✅ Coupled to what the user sees. Survives any refactor that keeps behaviour.
it("shows the menu after clicking the trigger", async () => {
render(<NavMenu />);
expect(screen.queryByRole("menu")).not.toBeInTheDocument();
await userEvent.click(screen.getByRole("button", { name: "Open menu" }));
expect(screen.getByRole("menu")).toBeInTheDocument();
});useState jadi useReducer, atau library menu-nya diganti sepenuhnya. Itulah intinya.Tugas praktik
Tulis test untuk component Callout di proyek ini: render satu dengan sebuah judul dan children, lalu pastikan judulnya ada dan children-nya ter-render. Pakai getByRole di mana bisa. Lalu ubah <aside>-nya jadi <div> di component-nya dan lihat apakah test-mu menyadarinya — kalau tidak, query-mu terlalu longgar.
Hasil yang diharapkan
Sebuah test yang lolos, dan yang gagal saat elemen landmark-nya berubah.