Mengapa halaman perlu diuji
Kamu sebenarnya sudah menguji sejak bab 0. Halaman dibuka, tombol ditekan, form diisi, hasilnya dilihat. Itu testing manual, menguji dengan tangan dan mata. Cara ini jalan, dan bab-bab sebelumnya memakainya.
Masalahnya muncul saat halaman tumbuh. Halaman dengan dua puluh fitur tidak bisa diperiksa semuanya setiap kali ada perubahan, waktunya tidak cukup. Pemeriksaan akhirnya dipangkas, dan bagian yang terlewat rusak diam-diam. Rusaknya baru terasa saat pembaca menemukannya, bukan saat kamu.
Testing front end adalah jawabannya. Pemeriksaan yang tadinya dijalankan dengan tangan ditulis jadi kode, disebut test. Komputer yang menjalankannya, berulang-ulang, dengan urutan dan hasil yang sama setiap kali. Test adalah kode yang memeriksa kode.
Test paling sederhana: membandingkan harapan dan hasil
Bentuk dasar semua test satu kalimat, kalau inputnya ini, hasilnya harus itu. Fungsi normalkanEmail menerima " A@B.COM ", harapannya "a@b.com". Kalau hasil fungsi sama dengan harapan, test lolos (pass). Kalau beda, test gagal (fail).
Pernyataan hasilnya harus sama dengan ini disebut assertion. Test yang menguji satu bagian kecil secara terpisah, satu fungsi misalnya, disebut unit test. Fungsi yang paling mudah diuji adalah fungsi murni, yang hasilnya hanya bergantung pada input dan tidak menyentuh halaman. Validasi form di bab 13 punya bentuk seperti itu, jadi dia titik mulai yang baik.
Regresi, bug lama yang kambuh
Ada pola rusak yang paling sering di halaman. Satu bug diperbaiki, lama-lama hilang, lalu beberapa perubahan kemudian muncul lagi. Perbaikan di form memecah daftar, pembenahan tombol merusak total keranjang. Pola ini disebut regresi, bug yang sudah pernah diperbaiki tapi kembali karena perubahan lain.
Test adalah jaring pengamannya. Kebiasaannya sederhana, setiap bug yang ditemukan ditulis jadi satu test yang menegaskan perbaikannya. Test lama tidak pernah dibuang, semuanya dijalankan setiap ada perubahan. Kalau perubahan baru membangunkan bug lama, test berubah merah beberapa detik setelah disimpan, bukan pembaca yang menemukannya dua minggu kemudian.
Tiga lapis pengujian
Halaman bisa diuji dari tiga ketinggian. Unit test menguji satu bagian kecil secara terpisah, satu fungsi normalisasi email, satu penghitung. Cepat, murah, jumlahnya bisa banyak. Integration test menguji beberapa bagian yang bekerja sama, misalnya form, validasi, dan pesan errornya. End-to-end test, sering disingkat e2e, menguji halaman dari luar seperti pengguna, browser sungguhan membuka halaman, mengisi form, menekan tombol, lalu hasil akhirnya dicek.
Semakin ke atas, test makin mirip pengguna, tapi makin lambat dan makin mahal dirawat. Porsi yang lazim menyerupai piramida, unit banyak di dasar, e2e sedikit di puncak. Karena itu bab ini fokus ke lapisan bawah, yang bisa dijalankan tanpa alat tambahan.
Di balik test runner
Test yang dijalankan komputer butuh pemandunya. Test runner adalah program yang mencari semua berkas test, menjalankan tiap test satu per satu, menghitung yang lolos dan gagal, lalu meringkasnya dalam satu baris. Vitest dan Jest adalah test runner yang umum di proyek front end, dan keduanya jalan di Node, JavaScript di luar browser.
Mesinnya sebenarnya sederhana, daftar test, satu loop yang menjalankan, penghitung hasil. Demo di bawah membangun versi mini dari mesin itu langsung di halaman, puluhan baris, supaya mesinnya terlihat, bukan tersembunyi di alat pemasangan.
Menguji komponen React
Komponen React adalah fungsi, sudah ditemu di bab 14, jadi dia bisa diuji juga. Pertanyaannya, apa yang ditegaskan. Yang paling tahan lama bukan isi state atau nama variabel, tapi apa yang pengguna lihat dan bisa lakukan, teks yang muncul, tombol yang jalan, pesan yang tampil.
Testing Library, pasangan umum React untuk pengujian, dibangun di prinsip itu. Dia merender komponen, memasangnya ke halaman, lalu mencari elemen cara pengguna mencarinya, lewat teks dan peran elemen, bukan lewat detail internal komponen. getByRole dan getByText mencari elemen, fireEvent meniru ketikan dan klik, expect menyatakan harapan. Peran elemen, role, sudah lewat di bab aksesibilitas. Kode ini jalan di test runner dengan lingkungan DOM tiruan, bukan di halaman publik, jadi di pasangan contoh ia ditampilkan, bukan dijalankan.
Analogi: bengkel mobil
Halamanmu adalah mobil. Unit test adalah mekanik yang mengecek satu komponen di bangku kerja, aki dicek satu, lampu dicek satu, cepat dan pasti. Integration test adalah merakit beberapa komponen jadi satu set lalu menyalanya, mesin dan transmisi harus cocok. End-to-end adalah uji jalan di jalan raya, mobil utuh dikendarai seperti pembeli. Regresi adalah kerusakan lama yang kambuh. Bengkel menjaganya dengan buku riwayat, setiap servis semua pemeriksaan lama diulang dari daftar, supaya yang pernah rusak tidak lolos lagi. Test suite, kumpulan semua testmu, adalah buku riwayat itu.
Pasangan contoh
Contohnya fungsi normalkanEmail yang sama dengan di demo, memangkas spasi di ujung dan mengubah huruf jadi kecil, persiapan sebelum email disimpan seperti di bab 13. Vanilanya tiga test sederhana, fungsi asersi yang membandingkan hasil dengan harapan, dan tiga pemanggilan langsung yang mencatat PASS atau FAIL ke console. Harapan ditulis lebih dulu, kode fungsinya menyusul dibandingkan ke sana.
Kolom React menguji komponen FormEmail dengan Testing Library. Test me-render komponen, mencari kotak input lewat perannya, mengetik email berantakan lewat fireEvent, lalu menegaskan hasil bersihnya muncul di layar. Perhatikan testnya tidak membuka state React sama sekali, ia melihat halaman seperti pengguna. Kode ini membutuhkan test runner di Node, jadi di halaman ini ia hanya ditampilkan, demo di bawah menjalankan versi vanilanya.
Vanilla JavaScript
function normalkanEmail(email) {
return email.trim().toLowerCase();
}
function asersi(hasil, harapan) {
if (hasil === harapan) {
console.log("PASS: " + harapan);
} else {
console.error("FAIL: harapan " + harapan + ", hasil " + hasil);
}
}
asersi(normalkanEmail("A@B.COM"), "a@b.com");
asersi(normalkanEmail(" a@b.com "), "a@b.com");
asersi(normalkanEmail(" A@B.COM "), "a@b.com");
React
function FormEmail() {
const [email, setEmail] = React.useState("");
return (
<div>
<input value={email}
onChange={(e) => setEmail(e.target.value)} />
<p>Hasil bersih: {email.trim().toLowerCase()}</p>
</div>
);
}
test("email berantakan dibersihkan", () => {
render(<FormEmail />);
fireEvent.change(screen.getByRole("textbox"), {
target: { value: " A@B.COM " },
});
expect(
screen.getByText("Hasil bersih: a@b.com")
).toBeTruthy();
});
Perbedaan inti bab ini ada di sini. Di vanilla, pemeriksaan ditulis dari bahan paling dasar, nilai yang diharapkan dan satu fungsi pembanding. Testing Library mengangkat levelnya, yang ditegaskan bukan nilai balik fungsi tapi apa yang terlihat di halaman. Keduanya berbagi satu bentuk yang sama, susun harapan, jalankan kode, bandingkan. Bentuk itulah testing, sisanya perkakas.
Demo Hidup
Demo di bawah adalah test runner mini yang jalan tanpa alat apa pun. Tekan Jalankan tes, tiga test untuk normalkanEmail dijalankan, semuanya hijau. Lalu tekan Rusak fungsi, tes ulang, fungsi sengaja dirusak dengan membuang pemangkasan spasi, dan dua test berubah merah. Itu regresi tertangkap oleh test, bukan oleh pembaca. Tekan sekali lagi untuk memperbaikinya.
Demo ini menjalankan test runner mini. Tiga test memeriksa fungsi normalkanEmail. Jalankan tesnya saat fungsi sehat, lalu rusak fungsinya dan jalankan lagi untuk melihat test menahan regresi.
Tekan Jalankan tes untuk memulai.
Begini cara baca demonya. Daftar test hanya data, tiap baris berisi nama, input, dan harapan. Tombol memanggil jalankanTes, yang melakukan satu loop, memanggil normalkanEmail dengan input, membandingkan hasilnya dengan harapan, lalu mencatat lolos atau gagal. Ringkasan di bawah daftar menghitung keduanya. Saat fungsinya dirusak, tidak ada satu test pun yang diubah, yang berubah hanya hasilnya. Perbedaan antara hasil dan harapan itulah yang dilaporkan merah.
Ringkasan bab ini. Testing front end memindahkan pemeriksaan manual jadi test, kode yang membandingkan harapan dengan hasil. Unit test menguji fungsi kecil, integration test menguji bagian yang bekerja sama, end-to-end menguji halaman utuh seperti pengguna, dan semuanya dijalankan oleh test runner. Regresi ditahan dengan kebiasaan sederhana, bug yang ditemukan jadi test yang tetap dijalankan. Bab berikutnya menjaga halaman tetap hidup tanpa jaringan: caching, PWA, dan offline.