Episode ini membahas optimasi runtime dan paralelisme test, reuse contexts dan fixtures dengan selective retries, pengurangan flaky tests lewat stable locators, serta penggunaan test.fixme dan test.skip secara strategis.

Test suite yang lambat membuat tim menghindarinya; test suite yang flaky membuat tim tidak mempercayainya. Episode 15 ini membahas dua masalah sekaligus: bagaimana membuat suite lebih cepat dan bagaimana membuatnya lebih andal — dua kualitas yang harus berjalan bersama.
Kecepatan dicapai dengan paralelisme yang tepat, reuse resource, dan menghindari pekerjaan berulang. Keandalan dicapai dengan locator yang stabil dan strategi skip yang jujur. Kalian akan belajar mengukur dan mengoptimalkan keduanya secara sistematis.
Langkah pertama optimasi adalah mengetahui ke mana waktu pergi. Report list menampilkan durasi tiap test:
npx playwright test --reporter=list--reporter=list menampilkan durasi tiap test. Identifikasi test yang paling lambat — mereka adalah kandidat utama optimasi. Sering kali beberapa test menyumbang sebagian besar total runtime.
Untuk test yang lambat, buka trace dan amati bagian Network dan Timing. Sering penyebabnya adalah waitForTimeout yang panjang, navigasi yang tidak perlu, atau request yang lambat. Ganti sleep dengan menunggu kondisi, seperti yang sudah kita bahas di episode 5.
Paralelisme membantu, tapi dengan batasan:
export default defineConfig({
workers: process.env.CI ? 4 : undefined,
});workers: process.env.CI ? 4 : undefined memakai default di lokal dan membatasi 4 worker di CI. Terlalu banyak worker membuat CPU jenuh dan justru memperlambat; temukan jumlah optimal dengan mengukur.
Salah satu penghemat waktu terbesar adalah menghindari login berulang. Storage state yang kita bahas di episode 13 bisa dipakai ulang di banyak test:
import { test as base } from '@playwright/test';
export const test = base.extend({
page: async ({ page }, use) => {
await page.goto('/beranda');
await page.getByRole('button', { name: 'Masuk' }).click();
await use(page);
},
});Fixture kustom page menjalankan setup (di sini, login) untuk setiap test yang memintanya. Setup sekali, dipakai banyak test — mengurangi runtime total secara signifikan.
Retries di semua test membuang waktu pada test yang memang stabil. Sebaliknya, berikan retry hanya pada test yang dikenal flaky karena faktor eksternal:
test('pembayaran dengan provider eksternal', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'Sering timeout karena provider pihak ketiga',
});
test.setTimeout(60000);
});test.setTimeout(60000) memberi test tertentu waktu eksekusi lebih panjang tanpa mempengaruhi test lain. Gabungkan dengan retries di level project untuk menyeimbangkan kecepatan dan keandalan.
Sebagian besar flaky test berakar pada locator yang rapuh — selector yang bergantung pada struktur DOM yang berubah-ubah. Prioritas locator yang benar:
getByRole dengan accessible name — paling stabil karena berbasis makna.getByLabel untuk input form.getByText dan getByPlaceholder untuk konten.await page.locator('#sidebar > div:nth-child(3) > button').click();await page.getByRole('button', { name: 'Simpan' }).click();Locator berbasis getByRole('button', { name: 'Simpan' }) tidak patah saat markup berubah selama aksesibilitas namanya dipertahankan.
Untuk elemen interaktif yang penting, tambahkan data-testid di aplikasi dan jadikan itu kontrak antara developer dan test:
await page.getByTestId('submit-order').click();getByTestId('submit-order') mencari atribut data-testid secara default. Test-ID sengaja dibuat stabil — perubahan tampilan tidak mempengaruhinya, hanya perubahan nama testid yang memengaruhinya.
Jangan biarkan test untuk fitur yang belum diimplementasikan membuat suite merah:
test.skip('export data belum tersedia', async ({ page }) => {
await page.goto('/laporan');
await page.getByRole('button', { name: 'Export' }).click();
});test.skip(...) menandai test untuk dilewati, dengan catatan jelas di report. Ini menjaga suite hijau sambil mendokumentasikan bahwa ada kontrak yang belum dipenuhi.
test.fixme menunjukkan bahwa test itu benar tapi aplikasi sedang bermasalah — bersifat sementara dan harus segera diperbaiki:
test.fixme('logout setelah token expired', async ({ page }) => {
await page.goto('/profil');
await expect(page).toHaveURL(/login/);
});test.fixme(...) menandai test sebagai known issue — akan dilewati tapi terdaftar jelas. Bedanya dengan skip: fixme adalah alarm yang harus diselesaikan, bukan kondisi permanen.
Skip dan fixme adalah alat disiplin, bukan jalan pintas. Tinjau secara berkala: setiap sprint, audit berapa banyak test yang di-skip dan kenapa. Test yang terus di-skip tanpa alasan jelas adalah sinyal bahwa coverage sedang bocor diam-diam.
npx playwright test --listnpx playwright test --list menampilkan semua test termasuk yang di-skip dan di-fixme — alat cepat untuk audit kondisi suite.
Episode 15 mengajarkan optimasi yang seimbang: mengukur durasi untuk menemukan hotspot, memakai storage state dan fixtures untuk memotong setup berulang, retries selektif untuk test yang memang butuh, locator stabil untuk menumpas flakiness, dan skip serta fixme yang jujur untuk menjaga suite tetap bermakna.
Inti yang harus dibawa pulang:
getByRole dan getByLabel untuk locator yang stabil.getByTestId menciptakan kontrak yang tidak patah oleh perubahan tampilan.test.skip dan test.fixme harus ditinjau secara berkala.Di episode 16 selanjutnya kita akan membahas visual regression testing — menangkap baseline screenshot, membandingkan visual diff dengan toHaveScreenshot, menoleransi layout drift dengan thresholding, serta integrasi dengan tool visual regression.