Skip to content

Test E2E Hijau Tapi Salah: Pelajaran Uji Adversarial

Adityo Guni Waluyo

26 route dilaporkan MISS sementara test bilang hijau. Assertion Playwright yang salah bentuk tidak pernah mengecek apa pun, dan adversarial run yang mengungkapnya.

Ringkasan

Test routing-crawl PortalKota full hijau padahal 26 route MISS, dan biang keroknya cuma satu baris: toBeAttached() dipanggil tanpa expect(), jadi cuma promise yang langsung resolve tanpa ngecek apa-apa. Akibatnya test selalu hijau walau selector salah atau halaman blank. Solusinya tiap spec harus bisa merah pas disabotase, kalau nggak, hijau itu cuma hiasan.

Routing-crawl test di PortalKota terakhir kali jalan full hijau. Puluhan langkah, nol kegagalan. Saya buka ringkasan coverage-nya: 26 route bertuliskan MISS. Dua angka itu tidak mungkin benar berduaan, dan yang salah ternyata bukan aplikasinya.

Tebakan pertama saya: DOM admin CMS-nya berubah, jadi selektor Playwright tidak nemu elemen. Saya tambahin log di blok catch, cetak err.message, berharap dapat nama selector yang gagal. Yang keluar malah tidak ada apa-apa. Blok catch itu tidak pernah ke-trigger sekali pun, padahal semua route dilaporkan MISS dalam hitungan milidetik.

Masalahnya ada di satu baris. Saya memanggil locator.toBeAttached() langsung sebagai method, tanpa membungkusnya di dalam expect(). Locator object punya method itu sebagai method yang mengembalikan promise. Dipanggil begitu saja, dia resolve seketika, tidak mengecek apa pun, tidak pernah melempar error. Test tetap hijau karena memang tidak ada assertion yang jalan. Laporan MISS itu justru datang dari kode pelaporan kita sendiri, bukan dari kegagalan test.

Baris yang salah itu bentuknya begitu saja begini:

// salah: promise doang, tidak pernah assert
await page.getByTestId('page-shell').toBeAttached();

Bentuk yang benar, satu wrapper plus budget waktu eksplisit:

await expect(page.getByTestId('page-shell').first())
  .toBeAttached({ timeout: 45_000 });

Bedanya bukan kosmetik. Web-first assertion di Playwright itu menarik ulang elemennya dan mengecek ulang kondisinya terus-menerus sampai terpenuhi atau timeout, default-nya 5 detik [2]. toBeAttached memastikan elemennya benar-benar nempel di DOM. Locator memang jantungnya auto-waiting dan retry-ability Playwright [1], tapi retry itu cuma jalan lewat assertion yang benar bentuk. Tanpa expect(), tidak ada yang menarik ulang, tidak ada yang mengecek, tidak ada yang bisa gagal. Selector salah, halaman blank, server 500 — semuanya tetap hijau.

Salahnya bukan cuma di assertion

Begitu satu lubang ketahuan, adversarial run berikutnya menemukan lubang lain yang lebih dangkal. Filter jenis di PortalKota pakai dua macam input yang cara kerjanya kebalikan.

Yang pertama