Mô hình trang và nhà máy trang trong Selenium

Mô hình trang (POM) và Nhà máy trang (Page Factory) trong Selenium: Hướng dẫn chi tiết

Trước khi tìm hiểu về Mô hình trang, chúng ta cần trả lời: Tại sao cần sử dụng POM?

Khởi tạo automation giao diện người dùng với Selenium WebDriver không quá phức tạp. Bạn chỉ cần tìm các phần tử giao diện và thực hiện thao tác trên chúng. Ví dụ:

driver.findElement(By.id("username")).sendKeys("test");
driver.findElement(By.name("password")).sendKeys("123456");
driver.findElement(By.xpath("//button[@type='submit']")).click();

Đây là kịch bản đơn giản. Tuy nhiên khi hệ thống mở rộng, việc bảo trì mã nguồn trở nên khó khăn. Nếu 10 kịch bản khác nhau sử dụng cùng một phần tử giao diện, bất kỳ thay đổi nào trong phần tử đó đều yêu cầu cập nhật 10 kịch bản - điều này tốn thời gian và dễ gây lỗi.

Giải pháp tối ưu là tạo lớp riêng biệt chứa các phần tử giao diện và phương thức tương tác. Lớp này có thể tái sử dụng cho nhiều kịch bản. Khi phần tử thay đổi, bạn chỉ cần cập nhật tại một vị trí duy nhất. Đây chính là Mô hình trang (Page Object Model - POM), giúp mã nguồn đọc dễ hơn, bảo trì dễ dàng hơntái sử dụng được.

Khái niệm POM

  • Mô hình trang là mẫu thiết kế tạo thư viện phần tử cho giao diện web
  • Mỗi trang web sẽ có lớp tương ứng chứa các phần tử và phương thức thao tác
  • Tên phương thức nên mô tả hành động cụ thể, ví dụ: choManHinhThanhToanHienThi()

Lợi ích của POM

  1. Tách biệt thao tác và kiểm tra giao diện, giúp mã nguồn gọn gàng
  2. Thư viện phần tử độc lập với testcase, có thể tích hợp với nhiều framework như TestNG/JUnit hoặc JBehave/Cucumber
  3. Giảm kích thước mã nhờ tái sử dụng phương thức
  4. Tên phương thức mô tả hành động rõ ràng, dễ hiểu

Cách triển khai POM

Ví dụ triển khai đơn giản

TestCase: Truy cập trang demo Guru99:

Bước 1) Mở trang Guru99 Demo
Bước 2) Kiểm tra văn bản "Guru99 Bank" tồn tại
Bước 3) Đăng nhập hệ thống
Bước 4) Xác nhận trang chủ hiển thị "Manger Id : demo"

Lớp trang Đăng nhập

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class LoginPage {
    WebDriver driver;
    By txtUsername = By.name("uid");
    By txtPassword = By.name("password");
    By lblTitle = By.className("barone");
    By btnLogin = By.name("btnLogin");

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

    public void nhapTenDangNhap(String tenDangNhap){
        driver.findElement(txtUsername).sendKeys(tenDangNhap);
    }

    public void nhapMatKhau(String matKhau){
        driver.findElement(txtPassword).sendKeys(matKhau);
    }

    public void nhanNutDangNhap(){
        driver.findElement(btnLogin).click();
    }

    public String layTieuDeTrang(){
        return driver.findElement(lblTitle).getText();
    }

    public void thucHienDangNhap(String tenDangNhap, String matKhau){
        this.nhapTenDangNhap(tenDangNhap);
        this.nhapMatKhau(matKhau);
        this.nhanNutDangNhap();
    }
}

Lớp trang Chủ

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class HomePage {
    WebDriver driver;
    By lblTenDangNhap = By.xpath("//table//tr[@class='heading3']");

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

    public String layTenDangNhap(){
        return driver.findElement(lblTenDangNhap).getText();
    }
}

Testcase sử dụng POM

package test;

import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import pages.LoginPage;
import pages.HomePage;

public class TestDangNhap {
    WebDriver driver;
    LoginPage trangDangNhap;
    HomePage trangChu;

    @BeforeTest
    public void khoiTao(){
        driver = new FirefoxDriver();
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        driver.get("http://demo.guru99.com/V4/");
    }

    @Test(priority=0)
    public void kiemTraTrangChu(){
        trangDangNhap = new LoginPage(driver);
        Assert.assertTrue(trangDangNhap.layTieuDeTrang().toLowerCase().contains("guru99 bank"));
        trangDangNhap.thucHienDangNhap("mgr123", "mgr!23");
        trangChu = new HomePage(driver);
        Assert.assertTrue(trangChu.layTenDangNhap().toLowerCase().contains("manger id : mgr123"));
    }
}

Khái niệm Page Factory

Page Factory là cách triển khai tối ưu hóa POM trong Selenium WebDriver. Sử dụng @FindBy để xác định phần tử và initElements để khởi tạo.

Ví dụ với Page Factory

package PageFactory;

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

public class LoginPage {
    WebDriver driver;

    @FindBy(name="uid")
    WebElement txtUsername;

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

    @FindBy(className="barone")
    WebElement lblTitle;

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

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

    public void nhapTenDangNhap(String tenDangNhap){
        txtUsername.sendKeys(tenDangNhap);
    }

    public void nhapMatKhau(String matKhau){
        txtPassword.sendKeys(matKhau);
    }

    public void nhanNutDangNhap(){
        btnLogin.click();
    }

    public String layTieuDeTrang(){
        return lblTitle.getText();
    }

    public void thucHienDangNhap(String tenDangNhap, String matKhau){
        this.nhapTenDangNhap(tenDangNhap);
        this.nhapMatKhau(matKhau);
        this.nhanNutDangNhap();
    }
}

Testcase với Page Factory

package test;

import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import PageFactory.LoginPage;
import PageFactory.HomePage;

public class TestDangNhapVoiPageFactory {
    WebDriver driver;
    LoginPage trangDangNhap;
    HomePage trangChu;

    @BeforeTest
    public void khoiTao(){
        driver = new FirefoxDriver();
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        driver.get("http://demo.guru99.com/V4/");
    }

    @Test(priority=0)
    public void kiemTraTrangChu(){
        trangDangNhap = new LoginPage(driver);
        Assert.assertTrue(trangDangNhap.layTieuDeTrang().toLowerCase().contains("guru99 bank"));
        trangDangNhap.thucHienDangNhap("mgr123", "mgr!23");
        trangChu = new HomePage(driver);
        Assert.assertTrue(trangChu.layTenDangNhap().toLowerCase().contains("manger id : mgr123"));
    }
}

Kết luận

  1. Mô hình trang là mẫu thiết kế thư viện phần tử trong Selenium WebDriver
  2. POM giúp code kiểm thử dễ bảo trì và tái sử dụng
  3. Page Factory là cách triển khai tối ưu hóa POM
  4. AjaxElementLocatorFactory hỗ trợ tải chậm phần tử khi cần thiết

Thẻ: selenium mô-hình-trang nhà-máy-trang testng

Đăng vào ngày 8 tháng 6 lúc 18:13