Dokumentasi Catatan Teknik
Wawasan, tutorial, dan catatan mendalam seputar QA automation dan rekayasa perangkat lunak.
Membangun Arsitektur E2E Test Automation dari Nol dengan Playwright & POM
Terakhir diperbarui: 14 Agustus 2026Memahami Sinkronisasi SDLC & STLC Secara Mendalam dalam Quality Engineering
Terakhir diperbarui: 12 Agustus 2026Alur End-to-End QA Process: Dari Risk Analysis, Test Plan, hingga Eksekusi
Terakhir diperbarui: 10 Agustus 2026Mengapa PRD Sangat Penting: Menghindari Lingkaran Setan "Back-and-Forth" Development
Terakhir diperbarui: 08 Agustus 2026Integrasi Playwright ke CI/CD Pipeline dengan GitHub Actions & Docker
Terakhir diperbarui: 05 Agustus 2026Memanfaatkan Claude Code & MCP untuk Akselerasi Pekerjaan QA
Terakhir diperbarui: 03 Agustus 2026Membangun Arsitektur E2E Test Automation dari Nol dengan Playwright & POM
Dalam dunia pengujian perangkat lunak modern, memiliki kerangka kerja (framework) otomasi yang handal, mudah dipelihara (maintainable), dan terstruktur adalah kunci sukses sebuah produk digital. Playwright telah menjadi salah satu standar industri terdepan untuk E2E testing.
1. Best Practice Struktur Direktori (Folder Structure)
Untuk proyek skala menengah hingga enterprise, pemisahan tanggung jawab (separation of concerns) sangat krusial. Berikut adalah struktur folder standar yang lebih matang:
my-playwright-framework/
├── tests/
│ └── login.spec.ts # 3. Test Script (Eksekusi & Assertion)
├── pages/
│ └── LoginPage.ts # 2. Page Class (Logic & Aksi Halaman)
├── selectors/
│ └── loginSelectors.ts # 1. Selector / Locator Repository
├── utils/
│ └── dataHelper.ts # 4. Utilities / Helpers (Fungsi Reusable)
├── playwright.config.ts # Konfigurasi Global Playwright
└── package.json
2. Anatomi 3 Pilar POM (Selector, Page, & Test)
Penerapan Page Object Model yang ideal membagi komponen menjadi tiga lapisan yang jelas agar kode mudah dirawat:
A. Selector / Locator Layer
Menyimpan semua elemen DOM atau *selectors* di satu tempat terpusat. Jika UI berubah, Anda cukup mengubahnya di sini.
// selectors/loginSelectors.ts
export const LoginSelectors = {
usernameInput: "#username",
passwordInput: "#password",
loginButton: 'button[type="submit"]',
errorMessage: ".alert-danger"
};
B. Page Class Layer (Implementasi Getter)
Di sinilah Getter memegang peran penting. Menggunakan getter (`get`) untuk membungkus locator mengamankan elemen (enkapsulasi) dan membuat pemanggilan kode menjadi jauh lebih bersih.
// pages/LoginPage.ts
import { Page } from "@playwright/test";
import { LoginSelectors } from "../selectors/loginSelectors";
export class LoginPage {
private page: Page;
constructor(page: Page) {
this.page = page;
}
// Penggunaan Getter (Best Practice Enkapsulasi)
get usernameInput() { return this.page.locator(LoginSelectors.usernameInput); }
get passwordInput() { return this.page.locator(LoginSelectors.passwordInput); }
get loginButton() { return this.page.locator(LoginSelectors.loginButton); }
get errorMessage() { return this.page.locator(LoginSelectors.errorMessage); }
async navigate() {
await this.page.goto("/login");
}
async login(user: string, pass: string) {
await this.usernameInput.fill(user);
await this.passwordInput.fill(pass);
await this.loginButton.click();
}
}
C. Test Script Layer (Execution & Assertion)
Berkat penggunaan Getter di atas, kita dapat melakukan assertion (validasi ekspektasi) langsung ke elemen halaman dengan sintaks yang sangat natural dan elegan.
// tests/login.spec.ts
import { test, expect } from "@playwright/test";
import { LoginPage } from "../pages/LoginPage";
test("Gagal login memunculkan pesan error", async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.navigate();
await loginPage.login("wrong_user", "wrong_password");
// Assertion memanfaatkan Getter (Sangat Clean!)
await expect(loginPage.errorMessage).toBeVisible();
await expect(loginPage.errorMessage).toHaveText("Invalid credentials");
});
3. Mengapa Menggunakan Getter Menjadi Best Practice Utama?
Dalam JavaScript/TypeScript modern, Getter bukan hanya sekadar sintaks gula (syntactic sugar), melainkan strategi arsitektur untuk menerapkan Lazy Evaluation dan Encapsulation. Mari kita lihat perbandingannya secara mendalam:
| ❌ Tanpa Getter (Deklarasi Properti Biasa) | ✅ Dengan Getter (Best Practice) |
|---|---|
Kekurangan:
|
Kelebihan:
|
4. Memisahkan Logika dengan Helpers (DRY Principle)
Seringkali saat membuat *test automation*, kita butuh membuat email acak (random), memformat tanggal, atau logika kalkulasi lainnya. Jangan pernah menaruh fungsi ini di dalam Page Class! Page Class harus 100% fokus pada interaksi UI.
Gunakan fungsi independen di folder /utils atau /helpers. Mengapa? Agar fungsi tersebut bisa dipakai ulang (reusable) di file mana pun tanpa mengulangi penulisan kode (Don't Repeat Yourself).
// utils/dataHelper.ts
export function generateRandomEmail(): string {
const timestamp = new Date().getTime();
return `qa_tester_${timestamp}@example.com`;
}
Kesimpulan
Membangun otomasi bukan sekadar membuat skrip agar "bisa jalan". Dengan memisahkan Selector, menggunakan Getter (Lazy Evaluation) untuk performa dinamis yang tahan banting terhadap perubahan DOM, serta mendelegasikan logika generator data ke fungsi Helper, Anda menciptakan arsitektur kelas enterprise yang scalable, minim *flaky test*, dan sangat mudah dirawat oleh tim dalam jangka panjang.