NỘI DUNG BÀI HỌC

✅ Hiểu XPath là gì và tại sao gọi nó là vũ khí "hạng nặng" cuối cùng. ✅ Phân biệt XPath Tuyệt đối (Tuyệt đối tránh xa) và XPath Tương đối (Khuyên dùng). ✅ Bỏ túi "bảng cửu chương" cú pháp XPath từ cơ bản đến nâng cao. ✅ (QUAN TRỌNG) Làm chủ "Hệ tư tưởng Gia phả": Lội ngược dòng (Ancestor, Parent) và tìm kiếm anh em họ hàng (Sibling, Following). ✅ Viết kịch bản tổng hợp sử dụng XPath trong Playwright.

🧠 I. KHÁI NIỆM CỐT LÕI VỀ XPATH

 

1️⃣ XPath là gì?

Ở bài học trước, chúng ta đã làm quen với get_by_* (tìm theo góc nhìn người dùng) và CSS Selector (tìm siêu tốc dựa vào "ngoại hình"). Vậy còn XPath (XML Path Language) là gì?

 

  • Định nghĩa chuẩn: XPath (XML Path Language)  là một ngôn ngữ truy vấn được sinh ra để tìm kiếm và định vị chính xác bất kỳ thành phần (element) nào trên một trang web (hoặc file XML).

  • Nếu trang web của bạn là một thành phố khổng lồ vô cùng phức tạp, thì XPath chính là tọa độ GPS. Nó giúp bạn chỉ thẳng tay vào đúng vị trí của một cái nút bấm, một dòng chữ hay một tấm ảnh mà không bao giờ sợ nhầm lẫn.

💡 Sự thật phũ phàng: XPath chạy chậm hơn CSS một chút xíu (khoảng vài mili-giây, mắt thường khó nhận ra) và nhìn code khá "lằng nhằng". Vậy tại sao tester lại "tôn thờ" XPath? Vì nó có 2 siêu năng lực độc quyền:

  1. Tìm theo nội dung chữ (Text): Tìm một thẻ chứa đoạn chữ dài ngoằng hoặc chỉ chứa một từ khóa ẩn sâu bên trong.

  2. Lội ngược dòng (Gia phả): Khả năng đi từ phần tử Con nhìn ngược lên phần tử Cha, hoặc từ phần tử Anh tìm ra phần tử Em. CSS Selector hoàn toàn "bó tay" ở khoản này!

 

2️⃣ Cách XPath nhìn nhận trang web

XPath không quan tâm trang web của bạn có màu sắc đẹp hay xấu. Nó nhìn toàn bộ mã nguồn HTML dưới dạng một Cây gia phả (DOM Tree Structure):

  • Gốc (Root): Là thẻ <html> bọc ngoài cùng.

  • Nhánh (Nodes): Các mục lớn như <body>, <div>, <table>.

  • Lá (Leaves): Các chi tiết nhỏ nhất không chứa ai bên trong nó nữa (ví dụ: một từ đơn lẻ, một cái icon).

Để tìm đến một "cái lá", bạn phải đi theo một con đường (Path) từ gốc rẽ qua các nhánh. Đó là lý do nó có tên là X-Path.


🎯 II. XPATH TUYỆT ĐỐI VS XPATH TƯƠNG ĐỐI

Nếu coi XPath là bản đồ chỉ đường, chúng ta có 2 cách để viết tọa độ:

 

1️⃣ ❌ XPath Tuyệt đối (Absolute XPath) - TUYỆT ĐỐI TRÁNH XA!

  • Đặc điểm: Bắt đầu bằng một dấu gạch chéo /. Nó chỉ đường một cách máy móc từ gốc rễ trên cùng của trang web đi xuống từng bậc một.

  • Ví dụ: /html/body/div[2]/div[1]/form/input[3]

  • Tại sao lại "phế"? Giống như chỉ đường: "Từ cổng thành, đi thẳng qua 2 ngã tư, rẽ trái, vào hẻm 1, gõ cửa nhà số 3". Trông thì có vẻ rất chính xác, nhưng nếu ngày mai Dev thêm một cái banner quảng cáo (tức là xây thêm một ngã tư mới), Robot tự động hóa của bạn sẽ lạc đường ngay lập tức! Kịch bản test sẽ Fail đỏ lòm.

 

2️⃣ ✅ XPath Tương đối (Relative XPath) - CHÂN ÁI LÀ ĐÂY!

  • Đặc điểm: Bắt đầu bằng hai dấu gạch chéo //. Bỏ qua gốc rễ, nó đóng vai trò như một chiếc trực thăng "nhảy dù" thẳng xuống nơi có phần tử cần tìm.

  • Ví dụ: //input[@id='email']

  • Tại sao nên dùng? Giống như việc bạn gọi taxi và nói: "Chở tôi đến thẳng cái nhà có ID là 'email'". Kệ cho Dev có bứng cái nhà đó đi đâu hay thêm bớt giao diện ra sao, Robot vẫn luôn đánh hơi và tìm được đúng mục tiêu.


🏛️ III. "BẢNG CỬU CHƯƠNG" CÚ PHÁP XPATH CƠ BẢN VÀ NÂNG CAO

 

Để viết XPath mượt mà, bạn chỉ cần nhớ một công thức cốt lõi duy nhất:

👉 //TênThẻ[@ThuộcTính='Giá Trị']

 

Dưới đây là tổng hợp mọi loại XPath tương đối từ cơ bản đến nâng cao bạn sẽ dùng khi làm nghề:

Loại tìm kiếm HTML Thực Tế XPath Tương Đối Diễn giải
Theo ID <input id="user"> //input[@id='user'] Dấu @ đại diện cho thuộc tính. Khuyên dùng nhất vì ID thường là duy nhất.
Theo Class <button class="btn"> //button[@class='btn'] Phải khớp chính xác toàn bộ chuỗi class.
Thuộc tính bất kỳ <input name="pwd"> //input[@name='pwd'] Có thể dùng với type, placeholder, data-testid...
Thẻ bất kỳ (*) <a class="link"> //*[@class='link'] Tìm bất kỳ thẻ nào mang class là 'link' (Bỏ qua Tên Thẻ).
Khớp một phần (Contains) <div class="btn-123"> //div[contains(@class, 'btn')] Cực kỳ quan trọng! Dùng khi Dev sinh ra các ID/Class có gắn số ngẫu nhiên thay đổi liên tục.
Bắt đầu bằng (Starts-with) <input id="user_01"> //input[starts-with(@id, 'user_')] Tìm những thẻ có ID bắt đầu bằng chữ "user_".
Chữ hiển thị (Text tuyệt đối) <a>Quên mật khẩu</a> //a[text()='Quên mật khẩu'] Phải khớp 100% từng chữ cái và không có khoảng trắng thừa.
Chữ hiển thị (Text chứa) <a>Xin chào Lan Hà</a> //a[contains(text(), 'Lan Hà')] Khuyên dùng thay vì text tuyệt đối để tránh lỗi do khoảng trắng hoặc dấu xuống dòng ẩn.
Kết hợp 2 điều kiện (AND) <input id="1" type="text"> //input[@id='1' and @type='text'] Ép phần tử phải thỏa mãn đồng thời cả 2 điều kiện.
Một trong hai điều kiện (OR) <input type="text"> //input[@type='text' or @type='email'] Thỏa mãn 1 trong 2 là được lấy.

 

🧭 IV. ĐỈNH CAO XPATH: ĐỊNH VỊ THEO "GIA PHẢ" (XPATH AXES)

Đây chính là linh hồn của XPath. Hãy dùng nó khi: Phần tử bạn muốn thao tác KHÔNG CÓ id/class rõ ràng, nhưng nó lại nằm cạnh một phần tử rất dễ tìm. Ta sẽ tìm phần tử dễ trước, rồi từ đó làm "bàn đạp" chiếu sang phần tử khó!

Để hiểu rõ cách XPath lội ngược, lội xuôi, chúng ta hãy coi cấu trúc HTML như một Cây Gia Phả. Hãy nhìn vào ví dụ sau, giả sử chúng ta lấy thẻ <span class="name">Nguyễn Văn A</span> làm BẢN THÂN (Bàn đạp) để đi tìm những người họ hàng xung quanh:

 

🧪 Cây Gia Phả HTML:

HTML
<div class="user-card">  <!-- 👴 TỔ TIÊN (Ông Nội): Chứa tất cả mọi người -->
    
    <h3>Hồ sơ</h3>       <!-- 🧔 BÁC: Khác cha, nằm trên -->
    
    <div class="details"> <!-- 👨 CHA RUỘT: Ôm trực tiếp các con vào lòng -->
        
        <label>Tên:</label>                           <!-- 👦 ANH RUỘT (Sinh trước) -->
        <span class="name">Nguyễn Văn A</span>        <!-- 🎯 BẢN THÂN (Làm bàn đạp) -->
        <button class="btn-edit">Sửa</button>         <!-- 👶 EM RUỘT (Sinh sau) -->
        
    </div>
    
    <div class="footer"> <!-- 👨 CHÚ: Khác cha, nằm dưới -->
        <button class="btn-delete">Xóa</button>       <!-- 🏃 NGƯỜI NỐI GÓT (Following) -->
    </div>

</div>

Chúng ta chia các từ khóa (Axes) thành 4 nhóm hướng đi. Cú pháp chung luôn là: 👉 //Bàn_Đạp/từ_khóa::Tên_Thẻ


🕵️‍♂️ NHÓM 1: NHÌN SANG NGANG (TÌM ANH EM RUỘT)

Điều kiện để làm anh em ruột: Phải nằm chung trong lòng một người Cha (thẻ bao bọc).

Từ khóa XPath Ý nghĩa (Ngôn ngữ Bình Dân) Ví dụ / Cách Robot di chuyển
following-sibling:: Tìm EM RUỘT: Tìm những thẻ cùng chung 1 Cha, nhưng nằm bên dưới (đẻ sau) Bản thân.

Đứng từ "Nguyễn Văn A", tìm nút "Sửa":


//span[text()='Nguyễn Văn A']/following-sibling::button


(Chiến thuật: Dùng khi Nhãn/Tên dễ tìm, còn ô nhập/nút bấm nằm kế bên thì bị ẩn danh).

preceding-sibling:: Tìm ANH RUỘT: Tìm những thẻ cùng chung 1 Cha, nhưng nằm bên trên (đẻ trước) Bản thân.

Đứng từ "Nguyễn Văn A", tìm ngược lên cái nhãn "Tên:":


//span[text()='Nguyễn Văn A']/preceding-sibling::label


(Chiến thuật: Hay dùng cho Checkbox nằm trước tên).

🕵️‍♂️ NHÓM 2: LỘI NGƯỢC DÒNG (TÌM BỀ TRÊN)

Độc chiêu của XPath, thứ mà CSS Selector không bao giờ làm được!

Từ khóa XPath Ý nghĩa (Ngôn ngữ Bình Dân) Ví dụ / Cách Robot di chuyển
parent::

Tìm CHA RUỘT: Lùi lên đúng 1 cấp, tóm lấy thẻ ôm trực tiếp Bản thân.


(Có thể viết tắt bằng /..)

Đứng từ "Nguyễn Văn A", tìm lên cái thẻ div .details chứa nó:


//span[text()='Nguyễn Văn A']/parent::div


Hoặc viết tắt: //span[text()='Nguyễn Văn A']/..

ancestor:: Tìm TỔ TIÊN: Lùi lên nhiều cấp (Cha, Ông Nội, Cố...). Tìm lớp vỏ bao bọc ngoài cùng.

Đứng từ "Nguyễn Văn A", tóm lấy toàn bộ thẻ bự .user-card (Ông nội):


//span[text()='Nguyễn Văn A']/ancestor::div[@class='user-card']


(Chiến thuật: Cực kỳ hữu dụng để tóm gọn cả 1 Table/Grid).

 

🕵️‍♂️ NHÓM 3: ĐI XUYÊN MÀN ĐÊM (KHÔNG CẦN CÙNG CHA)

Dùng khi phần tử muốn tìm nằm ở một nhánh khác, nhưng chắc chắn xuất hiện trước/sau Bản thân.

Từ khóa XPath Ý nghĩa (Ngôn ngữ Bình Dân) Ví dụ / Cách Robot di chuyển
following:: Người nối gót: Tìm TẤT CẢ thẻ xuất hiện phía sau Bản thân trong mã HTML.

Đứng từ "Nguyễn Văn A", nhìn xuyên nhánh để tóm nút "Xóa":


//span[text()='Nguyễn Văn A']/following::button[text()='Xóa']

preceding:: Người đi trước: Tìm TẤT CẢ thẻ xuất hiện phía trước Bản thân trong mã HTML.

Đứng từ nút "Xóa", nhìn ngược lên trên tìm chữ "Hồ sơ":


//button[text()='Xóa']/preceding::h3


🕵️‍♂️ NHÓM 4: NHÌN XUỐNG DƯỚI (TÌM CON CHÁU)

Bạn có thể viết tắt bằng dấu / hoặc // cho gọn.

Từ khóa XPath Ý nghĩa (Ngôn ngữ Bình Dân) Tương đương với cách viết tắt
child:: Tìm Con ruột: Nằm ngay dưới bụng 1 cấp.

Viết dài: //div[@class='details']/child::button


Viết tắt: //div[@class='details']/button

descendant:: Tìm Con Cháu: Nằm bên trong lòng, bất kể mấy cấp.

Viết dài: //div[@class='user-card']/descendant::button


Viết tắt: //div[@class='user-card']//button

 


🧾 V. VÍ DỤ THỰC CHIẾN: ĐƯA "GIA PHẢ" VÀO PLAYWRIGHT

Hãy xem xét bài toán Quản lý nhân sự: Có rất nhiều nút Xóa giống hệt nhau, làm sao để bấm đúng nút Xóa của "Nguyễn Văn A"?

 

🧪 Cấu trúc HTML (Giả lập):

HTML
<div class="user-row">
    <span class="name">Nguyễn Văn A</span>
    <span class="role">Tester</span>
    <button class="btn-delete">Xóa</button>
</div>
<div class="user-row">
    <span class="name">Trần Văn B</span>
    <span class="role">Dev</span>
    <button class="btn-delete">Xóa</button>
</div>

 

🧩 Kịch bản Playwright Python giải quyết bài toán:

from playwright.sync_api import Page

def test_xpath_gia_pha(page: Page):
    page.goto("https://admin-example.com")
    
    # -------------------------------------------------------------
    # 1. TUYỆT CHIÊU "EM RUỘT" (following-sibling)
    # BÀI TOÁN: Xóa Nguyễn Văn A
    # CHIẾN THUẬT: Lấy chữ "Nguyễn Văn A" làm Bàn Đạp, nhìn sang 
    # thằng "Em ruột" mang thẻ button bên dưới để click!
    # -------------------------------------------------------------
    nut_xoa_A = page.locator("//span[text()='Nguyễn Văn A']/following-sibling::button")
    nut_xoa_A.click()
    
    
    # -------------------------------------------------------------
    # 2. TUYỆT CHIÊU "CHA RUỘT" (parent:: hoặc /..)
    # BÀI TOÁN: Lấy toàn bộ thông tin của Trần Văn B
    # CHIẾN THUẬT: Đứng từ "Trần Văn B", lùi ra ngoài (tìm Cha ruột) 
    # để tóm trọn cái thẻ <div class="user-row"> chứa cả dòng.
    # -------------------------------------------------------------
    # Dùng cách viết tắt (/..) cho nhanh gọn:
    dong_cua_B = page.locator("//span[text()='Trần Văn B']/..")
    
    print("Thông tin dòng: ", dong_cua_B.text_content())


    # -------------------------------------------------------------
    # 3. TUYỆT CHIÊU KẾT HỢP CONTAINS
    # BÀI TOÁN: Tìm nút xóa có class chứa chữ "delete" (Phòng trường hợp class bị đổi)
    # -------------------------------------------------------------
    nut_xoa_B = page.locator("//span[text()='Trần Văn B']/following-sibling::button[contains(@class, 'delete')]")
    nut_xoa_B.click()

    print("✅ Đã thi triển thành công bí kíp Gia phả XPath!")

(Lưu ý của Playwright: Nếu chuỗi của bạn bắt đầu bằng // hoặc .., Playwright sẽ tự động hiểu đó là XPath mà không cần bạn phải khai báo dài dòng).


🧩 VI. TỔNG KẾT VÀ BÀI TẬP

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