E2E Testing dengan Playwright — Testing

Apa itu E2E Testing? End-to-End (E2E) testing menguji aplikasi dari sudut pandang user. Test membuka browser sungguhan, mengklik tombol, mengisi form, dan…

End-to-end testing (E2E) menguji aplikasi dari ujung ke ujung, persis seperti user: membuka browser sungguhan, mengisi form, mengklik tombol, lalu memeriksa apa yang tampil. Playwright adalah alat untuk menulis dan menjalankan test seperti itu di Chromium, Firefox, dan WebKit (mesin di balik Safari). Bayangin mystery shopper: orang yang disewa toko untuk belanja seperti pembeli biasa, dari pintu masuk sampai kasir, lalu melaporkan bagian mana yang macet. Test E2E adalah mystery shopper untuk aplikasimu.

🧪 Tebakan cepat: Sesudah tombol Masuk diklik, server butuh 1,5 detik sebelum tulisan "Selamat datang, Budi" muncul. Baris await expect(page.getByText('Selamat datang, Budi')).toBeVisible() langsung jalan begitu kliknya selesai. Hasilnya langsung gagal, langsung lulus, atau menunggu dulu? Tebak dulu, jawabannya ada di bagian "Assertion dan auto-wait".

Test pertama: login sampai dashboard

import { test, expect } from '@playwright/test'

test('login berhasil masuk ke dashboard', async ({ page }) => {
  await page.goto('/login')

  await page.getByLabel('Email').fill('[email protected]')
  await page.getByLabel('Password').fill('rahasia123')
  await page.getByRole('button', { name: 'Masuk' }).click()

  await expect(page).toHaveURL('/dashboard')
  await expect(page.getByText('Selamat datang, Budi')).toBeVisible()
})

Menyiapkan dan menjalankan Playwright

npm init playwright@latest memasang Playwright, membuat folder tests, dan file config:

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  testDir: './tests',
  use: { baseURL: 'http://localhost:3000' },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  ],
})

Locator: cara mencari elemen

Locator adalah alamat sebuah elemen. Urutan yang disarankan, dari yang paling mirip cara user melihat halaman:

  1. getByRole('button', { name: 'Masuk' }): peran elemen dan nama yang dibaca screen reader.
  2. getByLabel('Email'), getByPlaceholder(...), getByText('Halo'): teks yang terlihat user.
  3. getByTestId('login-button'): atribut data-testid yang sengaja dipasang untuk test.
  4. locator('.btn-primary'): selector CSS. Paling rapuh, karena class sering berubah waktu desain diganti, padahal tombolnya tetap sama di mata user.

Kalau locator-nya gak ketemu, aksinya menunggu terus sampai batas waktu test (30 detik) habis: Error: locator.click: Test timeout of 30000ms exceeded. dengan call log - waiting for locator('.btn-primary').

Aksi: goto, fill, click

Assertion dan auto-wait

Assertion Lulus kalau
await expect(page).toHaveURL('/dashboard') alamat halaman cocok
await expect(locator).toBeVisible() elemennya ada dan terlihat
await expect(locator).toHaveText('Dashboard') teks elemennya cocok

Ini jawaban tebakannya: menunggu dulu. Assertion Playwright mengecek berulang (di 0, 100, 350, 850, 1.850 ms, lalu tiap detik) sampai cocok atau batas 5 detik habis. Teksnya muncul di 1,5 detik, jadi pengecekan di 1.850 ms lulus. Ini yang disebut auto-wait, dan bersama dukungan tiga browser bawaan, ini keunggulan Playwright dibanding Selenium. Kalau server butuh 6 detik, barulah gagal:

Running 1 test using 1 worker

  ✘  1 [chromium] › tests/login.spec.ts:3:5 › login berhasil masuk ke dashboard (5.2s)


  1) [chromium] › tests/login.spec.ts:3:5 › login berhasil masuk ke dashboard ──────────────────────

    Error: expect(locator).toBeVisible() failed

    Locator: getByText('Selamat datang, Budi')
    Expected: visible
    Timeout: 5000ms
    Error: element(s) not found

    Call log:
      - Expect "toBeVisible" with timeout 5000ms
      - waiting for getByText('Selamat datang, Budi')


       9 |
      10 |   await expect(page).toHaveURL('/dashboard')
    > 11 |   await expect(page.getByText('Selamat datang, Budi')).toBeVisible()
         |                                                        ^
      12 | })
      13 |
        at /home/budi/toko/tests/login.spec.ts:11:56

    Error Context: test-results/login-login-berhasil-masuk-ke-dashboard-chromium/error-context.md

  1 failed
    [chromium] › tests/login.spec.ts:3:5 › login berhasil masuk ke dashboard ───────────────────────

Di latihan: page tiruan

Sandbox latihan gak punya browser. Jadi kamu menulis objek page tiruan dengan method yang dipanggil test-nya:

const page = {
  url: '',
  async goto(target) { this.url = target },
  async click(selector) { if (selector.includes('login')) { this.url = '/dashboard' } },
  async waitForURL(target) { return this.url === target },
}

it('pindah ke dashboard setelah klik login', async () => {
  await page.goto('/login')
  await page.click('#login-button')
  await page.waitForURL('/dashboard')
  expect(page.url).toBe('/dashboard')
})

Latihan pertama malah gak butuh page sama sekali: kamu menulis teks locator-nya, misalnya getLocator("role", "button") mengembalikan getByRole("button"), dan bestLocator memilih role dulu, lalu text, baru testId.

⚠️ Jebakan yang sering ketemu

TL;DR: Test E2E memakai browser sungguhan seperti user. Di Playwright, test('...', async ({ page }) => { ... }) membuka satu tab: await page.goto('/login'), await page.getByLabel('Email').fill(...), await page.getByRole('button', { name: 'Masuk' }).click(), lalu await expect(page).toHaveURL('/dashboard') dan await expect(locator).toBeVisible() atau toHaveText(...). Pilih locator berurutan: role, lalu label atau teks, lalu test id, dan CSS paling akhir. Assertion dan aksi menunggu sendiri (auto-wait, 5 detik untuk expect, 30 detik untuk test), dan test bisa jalan di Chromium, Firefox dan WebKit sekaligus. Jalankan dengan npx playwright test. Di sandbox latihan, page ditiru dengan objek biasa.

Yang akan kamu pelajari