# Page Object Model (POM) Là Gì? Mẫu Thiết Kế Automation

> Khi học viên của tôi viết script automation đầu tiên, code chạy được nhưng chỉ vài tuần sau là rối tung mỗi lần giao diện đổi. Page Object Model (POM) là một design pattern automation mô hình hóa mỗi …

- **URL canonical**: https://itlearn.edu.vn/blog/page-object-model
- **Published**: 2026-07-20T11:00:00+07:00
- **Modified**: 2026-07-21T11:33:24+07:00
- **Author**: Anh Tuấn
- **Category**: Automation test (https://itlearn.edu.vn/blog/cat/automation-test)
- **Reading time**: 10 phút
- **Source site**: IT LEARN — Học viện Software Testing tiếng Việt

---

Khi học viên của tôi viết script automation đầu tiên, code chạy được nhưng chỉ vài tuần sau là rối tung mỗi lần giao diện đổi. **Page Object Model (POM) là một *design pattern automation* mô hình hóa mỗi màn hình thành một class riêng, tách locator và thao tác ra khỏi test case — nhờ đó code dễ đọc, dễ tái sử dụng và cực dễ bảo trì.** Đây là *mô hình pom* phổ biến nhất trong test automation.

![page object model - sơ đồ tách page class chứa locator ra khỏi test class trong automation](/uploads/2026/07/page-object-model.jpg)

Trong bài này tôi đi từ khái niệm, qua lý do vì sao không dùng POM thì khổ, đến cấu trúc page class + test class, một ví dụ POM đầy đủ cho màn hình đăng nhập, và cuối cùng là Page Factory. Nếu bạn chưa rõ nền, hãy đọc qua [Automation testing là gì](/blog/automation-testing-la-gi) trước.

## Page Object Model là gì?

**Page Object Model (POM) là mẫu thiết kế trong đó mỗi trang (hoặc thành phần giao diện) của ứng dụng được biểu diễn bằng một class riêng.** Class đó chứa hai thứ: các *locator* (định vị phần tử trên trang) và các *method* thể hiện hành động người dùng có thể làm trên trang đó — ví dụ nhập username, bấm nút Đăng nhập.

Ý tưởng cốt lõi của *page object pattern* rất đơn giản: test case chỉ nên mô tả *"làm gì"* (đăng nhập với tài khoản A), còn *"làm bằng cách nào"* (tìm ô input bằng ID nào, bấm nút bằng selector nào) thì giấu hết vào page class. Test không được biết tới locator. Đây chính là nguyên tắc *separation of concerns* — tách mối quan tâm — áp dụng vào code kiểm thử.

Nói cho dễ hình dung: page class giống như một "điều khiển từ xa" của màn hình. Test viết `loginPage.login("tuan", "123")` mà không cần quan tâm bên trong nút bấm nằm ở đâu. Khi cần tra cứu các khái niệm liên quan, bạn có thể xem [thuật ngữ kiểm thử](/blog/thuat-ngu-kiem-thu/).

## Vì sao cần POM (vấn đề khi không dùng)

Hãy tưởng tượng bạn không dùng POM. Locator (ví dụ `driver.findElement(By.id("username"))`) nằm rải rác trong 50 test case khác nhau. Một ngày đẹp trời, dev đổi `id="username"` thành `id="user-name"`. Bạn phải sửa **50 chỗ** thủ công, sót một chỗ là test fail giả. Đây là ác mộng bảo trì kinh điển của automation.

POM giải quyết đúng nỗi đau này bằng nguyên tắc **DRY (Don't Repeat Yourself)**: mỗi locator chỉ khai báo *một lần duy nhất* trong page class. UI đổi thì bạn sửa một chỗ, toàn bộ test dùng lại được ngay. Bảng dưới so sánh trực tiếp hai cách viết, có thể trích dẫn được:

Tiêu chí

Không dùng POM

Dùng POM

Vị trí locator

Rải rác khắp test case

Tập trung trong page class

Khi UI đổi selector

Sửa nhiều nơi, dễ sót

Sửa một chỗ duy nhất

Trùng lặp code

Cao (copy-paste)

Thấp (tái sử dụng method)

Test case đọc có dễ không

Khó, lẫn kỹ thuật

Dễ, gần ngôn ngữ nghiệp vụ

Chi phí bảo trì lâu dài

Tăng nhanh theo số test

Ổn định, dễ mở rộng

Ba lợi ích lớn nhất tôi luôn nhấn với học viên: **dễ bảo trì** (sửa một chỗ), **tái sử dụng** (một method `login()` dùng cho hàng chục test), và **dễ đọc** (test đọc gần như tiếng người). Đó là lý do POM gần như là chuẩn mặc định trong mọi [test automation framework](/blog/test-automation-framework) nghiêm túc.

## Cấu trúc POM (page class + test)

Một dự án POM tối thiểu tách làm hai lớp rõ ràng:

- **Page class:** đại diện cho một màn hình. Bên trong khai báo locator (private) và các method public thể hiện hành động (`enterUsername`, `clickLogin`, `login`). Page class *không* chứa assertion (kiểm tra) — nó chỉ cung cấp hành động và trả về dữ liệu.

- **Test class:** chứa các test case thật. Nó gọi method của page class để thực hiện kịch bản, rồi *assert* kết quả. Đây là nơi duy nhất có mệnh đề kiểm tra (ví dụ `assertEquals`).

Ranh giới này quan trọng: **hành động ở page class, kiểm tra ở test class.** Trộn assertion vào page class là lỗi thường gặp khiến page object khó tái sử dụng. Với ứng dụng lớn, người ta còn thêm một lớp `BasePage` chứa những thao tác chung (mở URL, chờ phần tử) để mọi page class kế thừa — giảm trùng lặp thêm một tầng nữa.

## Ví dụ POM cho màn hình đăng nhập

Lấy ví dụ quen thuộc trong khóa học của tôi: "Hệ thống đặt phòng họp nội bộ", màn hình đăng nhập. Đây là page class viết bằng Selenium Java:

```java public class LoginPage { private WebDriver driver;

`// Locator khai báo tập trung — chỉ một chỗ
private By usernameInput = By.id("username");
private By passwordInput = By.id("password");
private By loginButton = By.cssSelector("button[type='submit']");
private By errorMessage = By.cssSelector(".alert-danger");

public LoginPage(WebDriver driver) {
 this.driver = driver;
}

// Method thể hiện hành động người dùng
public void login(String user, String pass) {
 driver.findElement(usernameInput).sendKeys(user);
 driver.findElement(passwordInput).sendKeys(pass);
 driver.findElement(loginButton).click();
}

public String getErrorMessage() {
 return driver.findElement(errorMessage).getText();
}
`

} ```

Và đây là test class dùng lại page object đó — sạch sẽ, gần như đọc thành lời:

```java @Test public void loginSaiMatKhau_hienThongBaoLoi() { LoginPage loginPage = new LoginPage(driver); loginPage.login("tuan", "sai-mat-khau");

`// Assertion nằm ở test class, KHÔNG ở page class
assertEquals("Sai tài khoản hoặc mật khẩu", loginPage.getErrorMessage());
`

} ```

Bạn thấy rõ chưa? Test chỉ nói "đăng nhập với mật khẩu sai rồi kiểm tra thông báo lỗi". Nếu mai này selector `#username` đổi, tôi chỉ sửa đúng dòng `usernameInput` trong `LoginPage`. Muốn nắm chắc cú pháp `By`, `findElement`, hãy ôn lại [Selenium cơ bản](/blog/selenium-co-ban).

## POM + Page Factory

**Page Factory là một lớp hỗ trợ có sẵn của Selenium (WebDriver) giúp khởi tạo các phần tử trong page object một cách gọn hơn**, dùng annotation `@FindBy` thay cho việc gọi `driver.findElement` thủ công. Nó là biến thể phổ biến của POM trong hệ Selenium Java.

Với Page Factory, page class được viết lại như sau:

```java public class LoginPage { @FindBy(id = "username") private WebElement usernameInput;

`@FindBy(id = "password")
private WebElement passwordInput;

@FindBy(css = "button[type='submit']")
private WebElement loginButton;

public LoginPage(WebDriver driver) {
 // Khởi tạo các @FindBy ở trên
 PageFactory.initElements(driver, this);
}

public void login(String user, String pass) {
 usernameInput.sendKeys(user);
 passwordInput.sendKeys(pass);
 loginButton.click();
}
`

} ```

`PageFactory.initElements(driver, this)` sẽ quét các trường có `@FindBy` và gắn WebElement tương ứng. Cú pháp gọn hơn, nhưng lưu ý: đây là tính năng đặc thù của **Selenium**. Các công cụ mới như Playwright hay Cypress **không** dùng Page Factory — với chúng, bạn viết POM theo cách "thủ công" (locator + method) như ví dụ đầu, thường tận dụng auto-wait sẵn có. POM là *mẫu thiết kế*, còn Page Factory chỉ là một *cách hiện thực* của riêng Selenium.

Lưu ý về thuật ngữ: ISTQB không định nghĩa riêng "Page Object Model" hay "Page Factory" — đây là mẫu thiết kế (design pattern) thuộc kỹ thuật lập trình test automation, không phải khái niệm trong syllabus Foundation. Tuy nhiên ISTQB nhấn mạnh test automation cần *khả năng bảo trì* (maintainability) và tách biệt tầng test — đúng tinh thần mà POM hướng tới.

## Câu hỏi thường gặp

### Page Object Model là gì?

Page Object Model (POM) là mẫu thiết kế trong automation, mô hình hóa mỗi màn hình của ứng dụng thành một class riêng. Class đó chứa locator và các method thể hiện hành động người dùng, tách hoàn toàn khỏi test case. Nhờ vậy code kiểm thử dễ đọc, dễ tái sử dụng và dễ bảo trì khi giao diện thay đổi.

### Vì sao nên dùng POM?

Vì POM tập trung mỗi locator vào một chỗ duy nhất theo nguyên tắc DRY. Khi giao diện đổi selector, bạn chỉ sửa một chỗ trong page class thay vì hàng chục test case. Ngoài ra POM giúp tái sử dụng method chung, giảm trùng lặp code, và làm test case đọc gần như ngôn ngữ nghiệp vụ, dễ hiểu hơn.

### POM dùng cho Selenium hay Playwright?

POM dùng được cho cả hai, vì nó là mẫu thiết kế độc lập với công cụ. Selenium thường kết hợp POM với Page Factory và annotation @FindBy. Playwright và Cypress cũng áp dụng POM nhưng viết thủ công locator cùng method, tận dụng cơ chế auto-wait sẵn có, và không dùng Page Factory của Selenium.

### Page Factory là gì?

Page Factory là lớp hỗ trợ có sẵn của Selenium giúp khởi tạo phần tử trong page object gọn hơn, dùng annotation @FindBy thay cho việc gọi driver.findElement thủ công. Bạn gọi PageFactory.initElements để gắn WebElement vào các trường đánh dấu @FindBy. Đây là biến thể phổ biến của POM, nhưng chỉ đặc thù cho hệ Selenium Java.

### POM có nhược điểm gì?

POM đòi thêm thời gian thiết kế ban đầu và tạo nhiều file class, nên với dự án nhỏ hoặc script dùng một lần có thể hơi thừa. Nếu lạm dụng, page class dễ phình to hoặc lẫn assertion vào trong, làm giảm khả năng tái sử dụng. Bù lại, với dự án lớn lâu dài, lợi ích bảo trì vượt xa chi phí này. Muốn dựng bộ automation theo POM bài bản trên dự án thật, bạn có thể tham khảo [khóa Automation](/playwright.html) của IT LEARN. Để chắc nền, hãy đọc thêm [Automation testing là gì](/blog/automation-testing-la-gi) và [test automation framework](/blog/test-automation-framework).

