NỘI DUNG BÀI HỌC

Nhận diện "Code rác": Hiểu vấn đề của việc viết Test thiếu kiến trúc.
Triết lý POM: Nắm vững sự tách biệt giữa WHAT (nghiệp vụ) và HOW (kỹ thuật).
Xây dựng BasePage: Gom nhóm các hành động chung (click, fill) để dùng lại.
Thiết kế Page Object: Tạo các lớp đại diện cho từng trang web cụ thể.
Tối ưu hóa: Kết nối POM với Test Case để viết code ngắn gọn, dễ bảo trì.
Thực hành: Xây dựng cấu trúc hoàn chỉnh cho LoginPageInventoryPage.

I. VẤN ĐỀ: KHI CODE LÀ MỘT "MỚ HỖN ĐỘN"

Hãy tưởng tượng bạn viết một kịch bản "Đăng nhập" bằng cách ghi tất cả các bước vào một file duy nhất:

Ví dụ:

def test_login(page):
    page.goto("https://www.saucedemo.com/")
    page.locator("#user-name").fill("standard_user")
    page.locator("#password").fill("secret_sauce")
    page.locator("#login-button").click()
    expect(page).to_have_url("https://www.saucedemo.com/inventory.html")

Tại sao cách này lại tệ?

  • "Sửa một chỗ, hỏng mười chỗ": Nếu lập trình viên đổi ID của ô nhập từ #user-name thành #username, bạn sẽ phải đi tìm và sửa tay ở hàng chục file test khác nhau.

  • "Đọc không hiểu gì": Một người mới nhìn vào sẽ thấy một đống các dòng lệnh kỹ thuật (click, fill) mà không hiểu được "ý đồ nghiệp vụ" (Đăng nhập, thêm hàng vào giỏ) là gì.

  • "Làm đi làm lại": Mỗi lần muốn test Đăng nhập, bạn lại phải chép lại y hệt 4 dòng trên.

 

2. Hệ quả

Vấn đề Mô tả Tác động
Locator trùng lặp Dùng cùng một selector ở nhiều nơi Khi UI thay đổi phải sửa hàng loạt test
Lẫn lộn nghiệp vụ và kỹ thuật Code test chứa cả hành động kỹ thuật (click, fill) và ý định nghiệp vụ (đăng nhập) Người đọc khó hiểu test đang kiểm tra điều gì
Mất tính tái sử dụng Phải viết lại thao tác tương tự cho nhiều test Tốn thời gian, dễ lỗi


3. Nguyên tắc DRY (Don’t Repeat Yourself)

Định nghĩa: Không lặp lại cùng một đoạn mã hoặc logic ở nhiều nơi.
Mục tiêu: Khi một hành vi (như login) thay đổi, chỉ cần chỉnh ở một vị trí duy nhất.
Ý nghĩa trong automation: Đảm bảo test framework dễ bảo trì, nhất quán và mở rộng lâu dài.

🏛️ II. GIẢI PHÁP: PAGE OBJECT MODEL (POM) LÀ GÌ?

1. Khái niệm


POM là một "phong cách" viết code thông minh. Thay vì viết lung tung, chúng ta chia mỗi trang web thành một đối tượng (Object) riêng biệt.

  • Mỗi trang web (Trang Login, Trang Sản Phẩm, Trang Giỏ Hàng) sẽ là một Class (một "tệp hồ sơ" riêng).

  • Trong đó, chúng ta chia làm 2 phần:

    1. Dữ liệu (Locators): Danh sách các nút bấm, ô nhập liệu (nếu ID thay đổi, ta chỉ cần sửa đúng 1 chỗ).

    2. Hành động (Business Actions): Các việc bạn làm trên trang (Ví dụ: Thay vì click 3 lần, bạn chỉ cần gọi 1 hàm login()).

  •  

2. Mục tiêu

  • Tách biệt WHAT và HOW:

    • WHAT: Ý định nghiệp vụ (“đăng nhập thành công”)

    • HOW: Cách thao tác kỹ thuật (“nhập username”, “click nút login”)

  • Giảm lặp lại code: Các hành động UI chỉ được định nghĩa một lần.

  • Tăng khả năng bảo trì: Khi locator thay đổi, chỉ cần cập nhật trong Page Object tương ứng.

  • Dễ đọc và dễ mở rộng: Test thể hiện ý định nghiệp vụ thay vì thao tác kỹ thuật.

III. CẤU TRÚC CHUẨN CỦA FRAMEWORK POM

Để dự án dễ quản lý, chúng ta chia theo 3 tầng (3 lớp):

  1. Tầng BasePage (Tầng móng): Gom tất cả các thao tác "nhàm chán" (click, điền text, mở web) vào một chỗ để dùng chung.

  2. Tầng Page Objects (Tầng nội thất): Mỗi file đại diện cho 1 trang web. Chứa các "kịch bản" riêng của trang đó (login, add_to_cart).

  3. Tầng Tests (Tầng người dùng): Nơi bạn viết test case. Code ở đây cực kỳ ngắn gọn, chỉ gọi tên hành động thay vì viết lệnh kỹ thuật.

tests/
│   test_login_pom.py
pages/
│   base_page.py
│   login_page.py


Lớp Nhiệm vụ chính Ví dụ
BasePage Chứa các thao tác Playwright cấp thấp, có thể tái sử dụng _click(), _fill(), _visit()
Page Object Mô tả nghiệp vụ của một trang cụ thể, kế thừa BasePage LoginPage.login(), InventoryPage.add_to_cart()
Test Case Gọi các hành động nghiệp vụ để kiểm thử chức năng test_login_successful()


IV. BASEPAGE – GOM NHÓM NHỮNG CÁI CƠ BẢN

Thay vì gọi lệnh Playwright trực tiếp, ta tạo một "ông chủ chung" là BasePage để quản lý các thao tác nền tảng:

  • Gom nhóm các hàm Playwright thường dùng (goto, click, fill, assert)

  • Thêm logic phụ trợ: logging, xử lý lỗi, timeout, hoặc wait tùy chỉnh.

  • Đảm bảo các Page Object tuân thủ cùng một chuẩn hành động.

from playwright.sync_api import Page, expect, Locator, TimeoutError

class BasePage:
    """Lớp cha chứa các hành động Playwright cơ bản, kế thừa cho mọi Page Object."""

    def __init__(self, page: Page):
        self.page = page

    def _visit(self, url: str):
        """Điều hướng tới URL được chỉ định."""
        print(f"[BasePage] Truy cập: {url}")
        self.page.goto(url, wait_until="domcontentloaded")

    def _get_locator(self, locator: str) -> Locator:
        """Trả về đối tượng Locator từ chuỗi selector."""
        return self.page.locator(locator)

    def _click(self, locator: str, name: str = ""):
        """Thực hiện click với xử lý lỗi và ghi log."""
        try:
            print(f"[Click] {name or locator}")
            self._get_locator(locator).click()
        except TimeoutError:
            print(f"[Lỗi] Không thể click vào {locator}")
            raise

    def _fill(self, locator: str, text: str, name: str = ""):
        """Điền dữ liệu vào ô input."""
        print(f"[Fill] '{text}' vào {name or locator}")
        self._get_locator(locator).fill(text)

    def _assert_text_visible(self, locator: str, text: str):
        """Kiểm tra văn bản mong đợi hiển thị trên giao diện."""
        print(f"[Assert] Kiểm tra '{text}' hiển thị")
        expect(self._get_locator(locator)).to_contain_text(text)



V. PAGE OBJECT – MÔ TẢ TRANG ĐĂNG NHẬP

1. Định nghĩa

Page Object là một lớp (class) mô tả toàn bộ hành vi nghiệp vụ của một trang web cụ thể.
Mỗi Page Object gồm hai phần chính:

  1. Locators – tất cả các selector (CSS/XPath) được khai báo ở dạng hằng số.

  2. Business Actions – các hành động nghiệp vụ mô phỏng tương tác người dùng.

2. Mục tiêu

  • Gom nhóm tất cả các thao tác liên quan đến một trang duy nhất.

  • Giúp test chỉ gọi đến các hành động nghiệp vụ thay vì mã kỹ thuật chi tiết.

  • Khi UI thay đổi, chỉ cập nhật trong Page Object tương ứng.


3. Ví dụ – pages/login_page.py

from .base_page import BasePage
from playwright.sync_api import expect

class LoginPage(BasePage):
    # Phần 1: Gom các cái nút ở đây
    URL = "https://www.saucedemo.com/"
    USER_INPUT = "#user-name"
    PASS_INPUT = "#password"
    LOGIN_BTN = "#login-button"

    # Phần 2: Mô tả hành động
    def login(self, user, password):
        self.page.goto(self.URL)
        self._fill(self.USER_INPUT, user)
        self._fill(self.PASS_INPUT, password)
        self._click(self.LOGIN_BTN)


🚀 VI. KẾT QUẢ: TEST CASE TRỞ NÊN "SẠCH ĐẸP"


1. Cấu trúc Test

Test case chỉ nên mô tả luồng nghiệp vụ thay vì thao tác kỹ thuật.
Không được chứa Playwright API trực tiếp như locator, click, fill.

2. Ví dụ – tests/test_login_pom.py

from pages.login_page import LoginPage
from playwright.sync_api import expect

def test_login_success(page):
    login = LoginPage(page)       # 1. Gọi trang Login
    login.login("user", "pass")   # 2. Đăng nhập
    
    # 3. Kiểm tra (Chỉ còn mô tả ý định, không còn kỹ thuật)
    expect(page).to_have_url(".../inventory.html")
​

 

3. Đặc điểm

Tiêu chí Đặc điểm của test sạch
Ngắn gọn Không chứa locator hoặc lệnh Playwright thô
Dễ đọc Mô tả rõ ý định kiểm thử bằng ngôn ngữ nghiệp vụ
Dễ bảo trì Khi UI thay đổi, test không cần chỉnh sửa
Dễ mở rộng Có thể thêm trang hoặc nghiệp vụ khác mà không ảnh hưởng test cũ


VII. TỔNG KẾT KHÁI NIỆM TRONG POM

Khái niệm Định nghĩa Ý nghĩa trong automation

POM (Page Object Model)

Mô hình tổ chức test trong đó mỗi trang được biểu diễn bằng một class

Giúp tách biệt nghiệp vụ và kỹ thuật, dễ bảo trì

BasePage

Lớp cha chứa hành động cơ bản của Playwright

Chuẩn hóa thao tác, tránh lặp lại mã

Page Object

Class đại diện cho một trang cụ thể

Gom locator và hành động nghiệp vụ tại một nơi

Business Action

Hành động có ý nghĩa nghiệp vụ (vd: login, add_to_cart)

Là “ngôn ngữ” của test case, giúp test dễ đọc

Locator

Selector trỏ tới element trong UI

Quản lý tập trung, dễ thay đổi khi UI đổi

Test sạch (Clean Test)

Test chỉ mô tả nghiệp vụ, không chứa chi tiết kỹ thuật

Dễ đọc, dễ hiểu, dễ mở rộng


VIII. KẾT LUẬN

Page Object Model là nền tảng cốt lõi để xây dựng framework kiểm thử tự động bền vững.
Nó giúp tách biệt giữa logic kỹ thuật và nghiệp vụ, giảm thiểu trùng lặp mã, và tạo ra test dễ đọc, dễ mở rộng.

Việc nắm vững POM giúp học viên bước đầu hình thành tư duy kiến trúc framework, thay vì chỉ “ghi script chạy được”.

Teacher

Teacher

Hà Lan

QA Automation

With over 5 years of experience in web, API, and mobile test automation, built strong expertise in designing and maintaining automation frameworks across various domains and international projects. Committed to mentoring and knowledge sharing, I provide practical guidance and proven techniques to help aspiring testers develop their skills and succeed in the automation field.

Cộng đồng Automation Testing Việt Nam:

🌱 Telegram Automation Testing:   Cộng đồng Automation Testing
🌱 
Facebook Group Automation: Cộng đồng Automation Testing Việt Nam
🌱 
Facebook Fanpage: Cộng đồng Automation Testing Việt Nam - Selenium
🌱 Telegram
Manual Testing:   Cộng đồng Manual Testing
🌱 
Facebook Group Manual: Cộng đồng Manual Testing Việt Nam

Chia sẻ khóa học lên trang

Bạn có thể đăng khóa học của chính bạn lên trang Anh Tester để kiếm tiền

Danh sách bài học