Page Object Pattern using PageFactory Selenium 2026

Chào các bạn, đây là phiên bản cập nhật cho bài viết về Page Object Model (POM)PageFactory, phù hợp với các phiên bản Selenium mới nhất hiện nay (Selenium 4.x trở lên). Vào thời điểm 2026, Selenium đã có nhiều cải tiến mạnh mẽ giúp code gọn gàng và ổn định hơn.

Điểm mới trong cập nhật này:
  • Sử dụng Duration thay cho TimeUnit (đã cũ).
  • Giới thiệu Selenium Manager (tự động quản lý driver, không cần System.setProperty).
  • Tối ưu hóa Code với Fluent Interface.
  • Sử dụng WebDriverWait (Explicit Wait) chuẩn chỉnh hơn.

1. Cấu trúc Project

Chúng ta sẽ chia project thành các phần rõ ràng theo mô hình POM:

  • Pages: Chứa các class đại diện cho từng trang (LoginPage, HomePage...), nơi khai báo Element và Action.
  • Tests: Chứa các Test Case thực thi.

2. Class LoginPage (Trang Đăng Nhập)

Sử dụng @FindBy để định danh phần tử và PageFactory.initElements để khởi tạo.

package vn.haibgit.pages;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class LoginPage {
    
    private WebDriver driver;

    // Constructor: Khởi tạo PageFactory
    public LoginPage(WebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }

    // Định nghĩa Element với @FindBy
    @FindBy(name = "uid")
    @CacheLookup // Cache element nếu nó không thay đổi để tăng tốc độ
    private WebElement txtUserName;

    @FindBy(name = "password")
    @CacheLookup
    private WebElement txtPassword;

    @FindBy(name = "btnLogin")
    @CacheLookup
    private WebElement btnLogin;

    @FindBy(name = "btnReset")
    private WebElement btnReset;

    // --- Action Methods ---

    public void setUserName(String name) {
        txtUserName.clear(); // Best practice: Clear trước khi nhập
        txtUserName.sendKeys(name);
    }

    public void setPassword(String password) {
        txtPassword.clear();
        txtPassword.sendKeys(password);
    }

    // Trả về HomePage sau khi login thành công (Fluent Interface)
    public HomePage clickLogin() {
        btnLogin.click();
        return new HomePage(driver);
    }

    /**
     * Phương thức Login gộp cho tiện sử dụng
     */
    public HomePage login(String userName, String password) {
        setUserName(userName);
        setPassword(password);
        return clickLogin();
    }
}

3. Class HomePage (Trang Chủ)

Ở đây chúng ta thêm xử lý đợi thông minh (Explicit Wait) để đảm bảo trang đã load xong.

package vn.haibgit.pages;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration; // Import mới cho Selenium 4+

public class HomePage {

    private WebDriver driver;

    @FindBy(className = "heading3")
    @CacheLookup
    private WebElement txtWelcome;

    public HomePage(WebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }

    public boolean isPageLoaded() {
        // Sử dụng WebDriverWait với Duration (thay thế cho int/TimeUnit cũ)
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        try {
            // Đợi cho element hiển thị rõ ràng
            wait.until(ExpectedConditions.visibilityOf(txtWelcome));
            
            String welcomeText = txtWelcome.getText();
            return welcomeText.contains("Welcome To Manager's Page of Guru99 Bank");
        } catch (Exception e) {
            return false;
        }
    }
}

4. Class TestPOM (Viết Test Case)

Lưu ý quan trọng: TimeUnit.SECONDS đã bị loại bỏ/deprecated trong các bản mới. Chúng ta chuyển sang dùng Duration.ofSeconds(...).

package vn.haibgit.tests;

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 vn.haibgit.pages.HomePage;
import vn.haibgit.pages.LoginPage;
import java.time.Duration;

public class TestPOM {

    private WebDriver driver;
    // URL demo (có thể thay đổi tùy thời điểm)
    private String url = "http://demo.guru99.com/v4/"; 

    @BeforeMethod
    public void setUp() {
        // Selenium 4.x+: Không cần System.setProperty("webdriver.chrome.driver", ...)
        // Selenium Manager sẽ tự động tải driver tương thích.
        driver = new ChromeDriver();
        
        driver.manage().window().maximize();
        driver.get(url);

        // CẬP NHẬT QUAN TRỌNG: Sử dụng Duration thay vì TimeUnit
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
    }

    @Test
    public void testLoginSuccess() {
        LoginPage loginPage = new LoginPage(driver);

        // Thực hiện login và nhận về HomePage object
        HomePage homePage = loginPage.login("mngr588662", "YvUzUqa"); 

        // Verify kết quả
        Assert.assertTrue(homePage.isPageLoaded(), "Đăng nhập thất bại hoặc trang chủ chưa tải xong!");
    }

    @AfterMethod
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
}

5. Giải thích các thay đổi quan trọng (2026)

5.1. Duration thay cho TimeUnit

Trong các phiên bản Selenium cũ, chúng ta viết: driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

Nhưng hiện tại, syntax chuẩn là: driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));

5.2. Selenium Manager

Bạn không cần phải tải thủ công chromedriver.exe và set path nữa. Thư viện Selenium sẽ tự động phát hiện trình duyệt Chrome trên máy và tải driver phù hợp nhất. Code gọn hơn rất nhiều!

5.3. Tại sao dùng @CacheLookup?

Vẫn giữ nguyên giá trị: Giúp Selenium lưu trữ element trong bộ nhớ cache sau lần tìm kiếm đầu tiên. Cực kỳ hữu ích cho các phần tử tĩnh (Logo, Menu, Input field cố định) giúp tăng tốc độ test.


Chúc các bạn áp dụng thành công Page Object Model vào dự án của mình!

0 comments:

Post a Comment