Lời mở đầu
Chào các bạn, chào mừng quay trở lại series Selenium Automation!
Ở bài trước, chúng ta đã viết được những Test Case đầu tiên chạy “ngon lành”. Nhưng hãy thú thật với nhau nhé: Nếu dự án của bạn có 100 Test Case, và bỗng dưng Dev đổi ID của nút “Login”, bạn sẽ làm gì?
Bạn sẽ phải đi tìm và sửa lại ID đó ở… 100 nơi khác nhau? Đó là ác mộng của việc Hard-code Locator (nhúng cứng địa chỉ phần tử vào code test) mà người mới thường mắc phải.
Hôm nay, tôi sẽ giới thiệu cho các bạn Page Object Model (POM) – một Design Pattern (mẫu thiết kế) giúp code của bạn:
- Dễ bảo trì: Sửa 1 nơi, cập nhật mọi chỗ.
- Dễ đọc: Code test giống như tiếng Anh, người không biết code cũng hiểu.
- Tái sử dụng: Viết hàm Login 1 lần, dùng cho n Test Case.
Page Object Model là gì?
Hãy tưởng tượng trang web của bạn giống như một ngôi nhà có nhiều phòng (Login Page, Home Page, Product Page…).
Trong POM, chúng ta coi mỗi trang web (Page) là một Lớp (Class) trong Java.
- Các thành phần trên trang (Button, Textbox…) sẽ là Biến (Variables).
- Các hành động trên trang (Click, Type…) sẽ là Hàm (Methods).
Lúc này, file Test Script (nơi chứa @Test) sẽ không còn chứa các dòng driver.findElement(...) loằng ngoằng nữa. Nó chỉ gọi hàm từ Page Class xử lý thôi.
Nguyên tắc vàng:
Test Class: Chỉ chứa logic kiểm thử (Assert, Flow). Page Class: Chứa Locator và hành động thao tác với element.
Chuẩn bị cấu trúc dự án chuẩn POM
Trong Eclipse, chúng ta sẽ tổ chức lại Project Maven cũ cho gọn gàng hơn.
Cấu trúc thư mục
Bạn hãy tạo các Package trong thư mục src/test/java (hoặc tách Page ra src/main/java nếu muốn chuyên nghiệp hơn) như sau:
com.automation.pages: Chứa các Class đại diện cho các trang web (LoginPage, HomePage…).com.automation.tests: Chứa các Test Case chạy TestNG.com.automation.base: Chứa các cấu hình chung (Setup Driver).
Triển khai POM: Thực hành trên Automation Exercise
Chúng ta sẽ làm bài toán: Login vào trang https://automationexercise.com/login.
Bước 1: Tạo Page Class (LoginPage.java)
Trong package com.automation.pages, tạo class LoginPage.
package com.automation.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage {
WebDriver driver;
// 1. Khai báo Locators (Nên để private để bảo đảm tính đóng gói)
private By emailInput = By.xpath("//input[@data-qa='login-email']");
private By passwordInput = By.xpath("//input[@data-qa='login-password']");
private By loginButton = By.xpath("//button[@data-qa='login-button']");
// 2. Constructor: Để nhận driver từ Test Class truyền vào
public LoginPage(WebDriver driver) {
this.driver = driver;
}
// 3. Các hành động (Actions) - Public methods
public void enterEmail(String email) {
driver.findElement(emailInput).sendKeys(email);
}
public void enterPassword(String password) {
driver.findElement(passwordInput).sendKeys(password);
}
public void clickLogin() {
driver.findElement(loginButton).click();
}
// Hành động gộp (Optional): Giúp code test gọn hơn nữa
public void login(String email, String password) {
enterEmail(email);
enterPassword(password);
clickLogin();
}
}Bước 2: Tạo Test Class (LoginTest.java)
Trong package com.automation.tests, tạo class LoginTest.
package com.automation.tests;
import com.automation.pages.LoginPage;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.time.Duration;
public class LoginTest {
WebDriver driver;
LoginPage loginPage; // Khai báo đối tượng Page
@BeforeMethod
public void setup() {
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
driver.get("https://automationexercise.com/login");
// Khởi tạo Page Object và truyền driver vào
loginPage = new LoginPage(driver);
}
@Test
public void testValidLogin() {
// Nhìn xem, code test giờ đây rất dễ đọc!
loginPage.enterEmail("admin@example.com");
loginPage.enterPassword("password123");
loginPage.clickLogin();
// Hoặc dùng hàm gộp: loginPage.login("admin@example.com", "password123");
// Assert kết quả (Ví dụ kiểm tra title hoặc URL sau khi login)
// Assert.assertTrue(...);
}
@AfterMethod
public void tearDown() {
driver.quit();
}
}Góc tương tác: Các bạn có thấy sự khác biệt không? File Test bây giờ hoàn toàn không thấy bóng dáng của
By.idhayBy.xpath. Code rất sạch và dễ hiểu đúng không nào?
Nâng cao: Chuyển trang giữa các Page Object
Trong thực tế, khi click Login thành công, website sẽ chuyển sang HomePage. Vậy trong code POM xử lý thế nào?
Chúng ta sẽ sửa lại method clickLogin trong LoginPage để nó trả về đối tượng HomePage.
Cập nhật LoginPage.java:
// Giả sử bạn đã tạo class HomePage
public HomePage clickLogin() {
driver.findElement(loginButton).click();
return new HomePage(driver); // Trả về trang tiếp theo
}Cập nhật LoginTest.java:
@Test
public void testLoginChain() {
HomePage homePage = loginPage.clickLogin();
// Bây giờ bạn có thể dùng homePage để thao tác tiếp
// homePage.verifyUserLoggedIn();
}Lab thực hành: DemoBlaze Shopping Flow
Bây giờ đến lượt bạn thực hành. Hãy áp dụng POM cho site: https://www.demoblaze.com/
Yêu cầu xây dựng 2 Page Classes:
- ProductPage:
- Locator: Tên sản phẩm, Nút “Add to cart”.
- Action: Click vào sản phẩm, Click Add to cart.
- CartPage:
- Locator: Danh sách sản phẩm trong giỏ, Nút “Place Order”.
- Action: Kiểm tra sản phẩm tồn tại, Click Place Order.
Gợi ý code cho ProductPage:
public class ProductPage {
WebDriver driver;
By addToCartBtn = By.xpath("//a[text()='Add to cart']");
public ProductPage(WebDriver driver) {
this.driver = driver;
}
public void addToCart() {
driver.findElement(addToCartBtn).click();
// Xử lý Alert nếu có
try {
Thread.sleep(2000); // Demo wait
driver.switchTo().alert().accept();
} catch (Exception e) {}
}
}Mini Project cuối buổi
Để “tốt nghiệp” bài học này, bạn hãy tự tay làm một Mini Project nhỏ áp dụng mô hình POM đầy đủ.
Đề bài: Trang https://demo.nopcommerce.com/
- Tạo RegisterPage: Thực hiện đăng ký tài khoản.
- Tạo LoginPage: Đăng nhập với tài khoản vừa tạo.
- Tạo SearchPage: Tìm kiếm sản phẩm “Apple MacBook Pro”.
- Viết TestNG Test Class kết nối cả 3 trang trên thành một luồng hoàn chỉnh.
Checklist tự đánh giá:
- [ ] Không hard-code Locator trong file Test.
- [ ] Tên biến và hàm đặt rõ nghĩa (tiếng Anh).
- [ ] Code chạy mượt mà từ đầu đến cuối.
Tổng kết và bài tập về nhà
Page Object Model (POM) không chỉ là code, nó là tư duy tổ chức. Khi dự án phình to lên hàng nghìn Test Case, POM chính là “vị cứu tinh” giúp bạn quản lý mọi thứ trong tầm tay.
Tóm tắt kiến thức:
- Tách biệt
Locators(Page) vàLogic(Test). - Tính đóng gói (Encapsulation) trong Java được áp dụng triệt để.
- Cách truyền
drivergiữa các class.
Bài tập về nhà: Hãy refactor (viết lại) bài tập của buổi trước (bài cơ bản) sang mô hình POM. Đừng quên upload lên GitHub và chia sẻ link bên dưới để mình và cộng đồng cùng review nhé.
Chủ đề tiếp theo: Chúng ta sẽ học cách xử lý dữ liệu test chuyên nghiệp với Data Driven Testing (Đọc dữ liệu từ Excel/JSON). Đừng bỏ lỡ nhé!
Chúc các bạn code vui và không gặp bug!