Playwright Academy
0 dari 17
Fase 3 · Dunia NyataModul 10 dari 17

Page Object

Mengumpulkan locator dan aksi ke dalam class, supaya test rapi dan gampang dirawat.

Page Object mengumpulkan locator dan aksi sebuah halaman dalam satu class. Test cukup memanggil login_page.login("customer", "rahasia123"), sementara detail locator-nya disimpan di class.

Di dunia QA

Page Object mirip dokumen "daftar elemen per halaman" yang dipakai tim QA. Kalau tombol Masuk berganti nama, cukup ubah di satu dokumen itu, bukan di puluhan test case.

Kenapa butuh POM?

Misalnya 40 test login dengan cara seperti ini:

page.get_by_label("Username").fill("customer")  # isi Username
page.get_by_label("Password").fill("rahasia123")  # isi Password
page.get_by_role("button", name="Masuk").click()  # klik Masuk
await page.getByLabel('Username').fill('customer'); // isi Username
await page.getByLabel('Password').fill('rahasia123'); // isi Password
await page.getByRole('button', { name: 'Masuk' }).click(); // klik Masuk

Kalau tombol "Masuk" diganti menjadi "Login", kamu harus mengedit 40 file. Dengan POM, cukup ubah 1 baris di class LoginPage.

Tanpa POMDengan POM
Locator tersebar di banyak testLocator terkumpul di satu tempat
Test panjang dan teknisTest pendek dan mudah dibaca
Tampilan berubah → edit banyak fileTampilan berubah → edit satu class
Susah dipakai ulangAksi bisa dipakai di test mana saja

Struktur folder

Python
playwright-python/
├─ pages/
│  ├─ login_page.py      ← LoginPage
│  ├─ shop_page.py       ← ShopPage
│  └─ checkout_page.py   ← CheckoutPage
└─ tests/
   ├─ conftest.py        ← fixture yang membuat page object
   └─ academy/test_m10_pom.py

Supaya from pages.login_page import LoginPage bisa dipakai dari folder tests, tambahkan pythonpath = ["."] di bagian [tool.pytest.ini_options] pada pyproject.toml.

TypeScript
playwright-typescript/
├─ pages/
│  ├─ LoginPage.ts
│  ├─ ShopPage.ts
│  └─ CheckoutPage.ts
├─ fixtures.ts           ← custom fixture yang membuat page object
└─ tests/academy/
   └─ m10-pom.spec.ts

Page object pertamamu: LoginPage

pages/login_page.pypages/LoginPage.tsLulus test
Memuat pages/login_page.py…
Memuat pages/LoginPage.ts…

Isi sebuah page object biasanya ada tiga bagian:

  1. Constructor menerima page dan menyiapkan semua locator. Aman, karena locator baru mencari elemen saat dipakai (Modul 4).
  2. Method menggambarkan apa yang dilakukan pengguna: open(), login(), bukan klik_tombol_biru().
  3. Assertion utama tetap di test. Page object cukup menyediakan locator (misalnya error) untuk di-expect.

Page object yang lebih lengkap: ShopPage

pages/shop_page.pypages/ShopPage.tsLulus test
Memuat pages/shop_page.py…
Memuat pages/ShopPage.ts…

Method product(name) mengembalikan locator, bukan langsung mengklik, jadi test bebas memakainya, misalnya mengecek tombol yang tidak aktif di kartu "Tas Laptop".

Lihat juga CheckoutPage
pages/checkout_page.pypages/CheckoutPage.tsLulus test
Memuat pages/checkout_page.py…
Memuat pages/CheckoutPage.ts…

Menyiapkan page object lewat fixture

Supaya test tidak menulis LoginPage(page) berkali-kali, siapkan lewat fixture:

tests/conftest.pyfixtures.tsLulus test
Memuat tests/conftest.py…
Memuat fixtures.ts…

Fixture logged_in_shop memakai dua fixture lain (login_page, shop_page) untuk login lewat UI, lalu memberikan ShopPage yang siap dipakai. Jadi test yang butuh kondisi "sudah login" tinggal meminta logged_in_shop.

test.extend<Fixtures>({...}) membuat versi test baru yang punya fixture tambahan. Jadi test meng-import test dan expect dari fixtures.ts, bukan dari @playwright/test. Fixture loggedInShop memakai fixture lain (loginPage, shopPage) untuk login, lalu memberikan ShopPage yang siap dipakai.

Hasilnya: test yang enak dibaca

tests/academy/test_m10_pom.pytests/academy/m10-pom.spec.tsLulus test
Memuat tests/academy/test_m10_pom.py…
uv run pytest tests/academy/test_m10_pom.py -v
Memuat tests/academy/m10-pom.spec.ts…
npx playwright test m10-pom --project=chromium

Dibanding test login di Modul 7, test ini bisa dibaca orang yang tidak paham kode.

Kebiasaan baik saat memakai POM

LakukanHindari
Satu class per halaman (atau per bagian besar)Satu class raksasa untuk seluruh aplikasi
Nama method sesuai keinginan pengguna (add_to_cart)Nama teknis (click_button_3)
Locator disiapkan sekali di constructorLocator ditulis ulang di setiap method
Method kecil yang bisa digabung-gabungSatu method "do_everything()" yang panjang
Assertion utama ada di testexpect tersembunyi di setiap method
Bagian yang muncul di banyak halaman (header, keranjang) jadi class sendiriMenyalin locator header ke setiap page

Bagian yang muncul di banyak halaman, misalnya header dan panel keranjang, bisa jadi class sendiri (CartPanel) lalu dipasang di page object: self.cart = CartPanel(page). Cara ini disebut komposisi.

Tidak perlu berlebihan

Untuk project dengan 5 test, POM lengkap bisa berlebihan. Mulai sederhana, lalu pindahkan ke page object begitu kode yang sama mulai berulang.

Lebih jauh (opsional)Trik komunitas
  • Pakai fixture, bukan warisan bertingkat. Banyak tim lebih suka menyiapkan page object lewat fixture daripada membuat BasePage yang diwariskan berlapis-lapis dan susah diikuti.
  • Pecah jadi komponen kecil: Header, CartPanel, Modal. Page object tinggal menyusunnya.
  • Pakai describe() di constructor supaya setiap locator punya nama yang jelas saat dilihat di trace.
  • mergeTests() (TypeScript) menggabungkan beberapa file fixture, misalnya fixture UI dan fixture API, menjadi satu test.
  • Siapkan kondisi lewat API (Modul 13), lalu pakai page object hanya untuk bagian yang memang diuji lewat UI. Test jadi lebih cepat dan stabil.

Latihan

Latihan 1

Buat page object untuk formulir Arena

  1. Buat ArenaFormPage (Python: pages/arena_form_page.py, TypeScript: pages/ArenaFormPage.ts) dengan locator nama, bahasa, newsletter, tombol kirim, dan hasil.
  2. Tambahkan method fill_form(nama, level, bahasa, newsletter) / fillForm(...) dan submit().
  3. Daftarkan fixture-nya di tests/conftest.py / fixtures.ts.
  4. Tulis 2 test yang memakai fixture itu: satu tanpa newsletter, satu dengan newsletter.

Hasilkedua test lulus, dan test-nya tidak berisi locator sama sekali

Latihan 2

Page object untuk KlinikQA

Brief: booking di KlinikQA punya tiga langkah: pilih dokter, pilih jadwal dari kalender, lalu konfirmasi dengan keluhan. Page object menyembunyikannya di balik satu method.

  1. Buat KlinikPage dengan method login(...), choose_doctor(...) / chooseDoctor(...), choose_schedule(tanggal, jam) / chooseSchedule(...), book(dokter, tanggal, jam, keluhan) yang memakai keduanya, dan appointment(nomor) untuk mencari janji di halaman Janji temu saya.
  2. Tulis test: login sebagai pasien, buat janji temu dengan dr. Sari Lestari jam 10:30, lalu buka Janji temu saya dan pastikan janji itu berstatus Terjadwal.

Hasilnomor janji temu berawalan KQA-, antrean U-06 (jam 10:30 adalah slot ke-6), dan statusnya Terjadwal