Nếu làm automation test và code của bạn đang trong tình trạng "một mớ hỗn độn" - locator nằm rải rác khắp nơi, chỉ cần đổi cái ID trên giao diện là phải sửa đến 20 chỗ - thì bài viết này dành cho bạn. Mình sẽ giải thích về Page Object Model (POM), một trong những kĩ thuật đầu tiên mà bất kì ai học automation test cũng nên nắm.
Trước đây mình thường sử dụng Selenium Java cho Web Automation. Trong bài này mình chọn Playwright + TypeScript vì syntax gọn, dễ tiếp cận và cũng thuận tiện khi kết hợp với các công cụ AI hỗ trợ coding hiện nay.
Giả sử bạn viết test cho chức năng đăng nhập như sau:
test('login', async ({ page }) => {
await page.locator('#username').fill('admin');
await page.locator('#password').fill('abc123');
await page.locator('#loginBtn').click();
});
Trông cũng ổn đúng không? Nhưng thử xem nếu giờ bạn có 20 test cases khác cũng cần login trước khi test các chức năng khác. Nghĩa là đoạn code page.locator('#username')... này sẽ được copy&paste ra lên đến tận 20 lần.
Rồi sau đó vào ngày đẹp trời, Dev đổi id="username" thành id="user-email". Lúc này bạn sẽ phải mở 20 files ra và tìm sửa 20 chỗ. Đây chính là lúc bạn cần POM đấy.
POM là gì?
POM là cách bạn "gói" từng trang hoặc khu vực giao diện thành những object/class riêng. Ví dụ Login Page, Home Page, Cart Page... Trong đó chứa:
- Các locator của trang đó
- Các action thực hiện trên trang đó (input text, click button...) Sau đó, ở test case của bạn chỉ cần gọi các hành động này ra dùng, không cần biết bên trong nó tìm phần tử bằng ID hay XPath gì cả.
Bạn có thể xem POM như một cái remote điều khiển. Không cần biết bên trong tivi có mạch điện gì, chỉ cần bấm nút trên đó là được. Page Object chính là cái remote cho từng trang web.
Quay lại ví dụ login ở trên. Với POM, đầu tiên ta tạo một class riêng đại diện cho trang Login:
import { Page, Locator } from '@playwright/test';
export class LoginPage {
readonly page: Page;
// Bước 1: Khai báo các locator
readonly usernameField: Locator;
readonly passwordField: Locator;
readonly loginButton: Locator;
readonly errorMessage: Locator;
constructor(page: Page) {
this.page = page;
this.usernameField = page.locator('#username');
this.passwordField = page.locator('#password');
this.loginButton = page.locator('#loginBtn');
this.errorMessage = page.locator('.error-msg');
}
// Bước 2: Viết các action thực hiện trên page
async enterUsername(username: string) {
await this.usernameField.fill(username);
}
async enterPassword(password: string) {
await this.passwordField.fill(password);
}
async clickLogin() {
await this.loginButton.click();
}
// Bước 3 (tùy chọn): Gộp các step nhỏ thành 1 action lớn hơn
async login(username: string, password: string) {
await this.enterUsername(username);
await this.enterPassword(password);
await this.clickLogin();
}
}
Giờ đây, test case của bạn sẽ trông như thế này:
import { test, expect } from '@playwright/test';
import { LoginPage } from './LoginPage';
test('shows an error when login fails', async ({ page }) => {
const loginPage = new LoginPage(page);
await page.goto('https://example.com/login');
await loginPage.login('wronguser', 'wrongpass');
await expect(loginPage.errorMessage)
.toHaveText('Invalid username or password');
});
Ở test case vừa ngắn gọn và khi đọc vào là hiểu ngay. Không còn thấy page.locator('#...') rải rác khắp nơi, không còn thấy selector dài dòng nữa. Nếu thay đổi chỉ nằm ở locator, bạn chỉ cần sửa một nơi trong LoginPage thay vì sửa tất cả test case đang sử dụng nó.
Lí do bạn nên xài POM
- Sửa một chỗ, dùng khắp nơi: locator thay đổi thì chỉ cần cập nhật trong Page Object
- Code test đọc như văn xuôi: await loginPage.login(user, pass) dễ hiểu hơn nhiều so với đống page.locator(...)
- Tái sử dụng thoải mái: hàm login() viết một lần, dùng cho cả trăm test case
- Tách bạch rõ ràng: phần "test cái gì" (test case) và phần "thao tác ra sao" (Page Object) không bị lẫn vào nhau, dễ maintain về sau
POM không khó, chỉ là một cách tổ chức code gọn gàng hơn. POM không làm testcase của bạn "thông minh hơn", nó giúp code Automation dễ đọc, dễ tái sử dụng và dễ bảo trì hơn. Khi project càng lớn, lợi ích này càng rõ.

