NỘI DUNG BÀI HỌC

🧠 Hiểu rõ Web-First Assertions: Nắm vững cơ chế tự động thử lại (Auto-retrying) triệt tiêu lỗi Flaky.
🏗️ Áp dụng chuẩn mô hình AAA: Tổ chức cấu trúc mã nguồn test case khoa học và chuyên nghiệp.
🛠️ Làm chủ hệ thống expect: Vận dụng linh hoạt các hàm kiểm tra trạng thái hiển thị, nội dung văn bản và thuộc tính.
🪂 Kỹ thuật nâng cao: Sử dụng thành thạo Soft Assertions để tối ưu hóa việc kiểm thử giao diện (UI Testing).



⚖️ I. Bản Chất Cách Mạng Của "Web-First Assertions"

Trong ngôn ngữ lập trình Python tiêu chuẩn, lệnh assert sẽ kiểm tra giá trị ngay tại thời điểm dòng lệnh đó được chạy. Nếu điều kiện sai, chương trình quăng lỗi lập tức. (Ví dụ: assert page.locator("#status").is_visible() == True 👉 Rất dễ tạch nếu UI cần 1 giây để hiển thị).

🌟 Sự khác biệt của Playwright: Thư viện expect của Playwright mang tên Web-First Assertions. Nó không đánh rớt bài test ngay lập tức mà tích hợp sẵn cơ chế Auto-retrying (Tự động thử lại). Nó sẽ liên tục truy vấn trạng thái của trình duyệt trong một khoảng thời gian (mặc định là 5 giây). Nếu trong 5 giây đó phần tử chuyển sang trạng thái đúng, bài test sẽ vượt qua một cách mượt mà.

from playwright.sync_api import expect

# ❌ Không nên dùng (Dễ gây Flaky Test vì không có cơ chế chờ):
# assert page.locator(".success-alert").is_visible()

# ✅ Tiêu chuẩn Playwright (Thông minh: Chờ tối đa 5s cho đến khi hiển thị):
expect(page.locator(".success-alert")).to_be_visible()


🏗️ II. Cấu Trúc Vàng Trong Làng Viết Test: Mô Hình AAA

Mỗi Test Case chuyên nghiệp trên thế giới đều tuân thủ nghiêm ngặt mô hình cấu trúc 3 phần rõ ràng dưới đây:

  1. 📦 Arrange (Chuẩn bị): Khởi tạo dữ liệu, cấu hình môi trường, điều hướng đến trang cần kiểm thử.

  2. 🎬 Act (Hành động): Thực hiện chuỗi hành động tương tác (Điền form, Click chuột - Kiến thức Bài 4).

  3. ⚖️ Assert (Kiểm định): Sử dụng expect để xác thực kết quả thực tế với mong đợi (Kiến thức Bài 5).

    def test_login_flow(page):
        # 1. ARRANGE
        page.goto("https://example.com/login")
        
        # 2. ACT
        page.get_by_placeholder("Username").fill("admin_user")
        page.get_by_placeholder("Password").fill("Secret123")
        page.get_by_role("button", name="Login").click()
        
        # 3. ASSERT
        expect(page).to_have_url("https://example.com/dashboard")

     


🛠️ III. Hệ Thống Bản Đồ Thư Viện "Expect" Toàn Tập

👁️ 1. Kiểm tra trạng thái Hiển thị & Tương tác

Sử dụng nhóm này để kiểm tra tính hợp lệ của giao diện người dùng.

  • expect(locator).to_be_visible(): Xác minh phần tử phải hiển thị trên màn hình.

  • expect(locator).to_be_hidden(): Xác minh phần tử phải bị ẩn hoặc biến mất hoàn toàn khỏi DOM (Ví dụ: Chờ vòng xoay loading biến mất).

  • expect(locator).to_be_enabled(): Phần tử đang ở trạng thái hoạt động bình thường, sẵn sàng nhận click.

  • expect(locator).to_be_disabled(): Phần tử đang bị khóa (bị mờ đi), không cho phép người dùng click.

    .to_be_visible() / .to_be_hidden()

    • Ví dụ Automation:

      # Sau khi click submit, chờ vòng xoay loading biến mất
      submit_button.click()
      expect(page.locator(".loading-spinner")).to_be_hidden(timeout=10000) # Chờ tối đa 10s

       

    .to_be_enabled() / .to_be_disabled()

    • Ví dụ Automation:

      submit_button = page.get_by_role("button", name="Submit")
      
      # Lúc đầu, nút bị vô hiệu hóa
      expect(submit_button).to_be_disabled()
      
      # Sau khi điền đủ form
      page.locator("#agree-terms").check()
      
      # Nút được bật lên
      expect(submit_button).to_be_enabled()

     

🔤 2. Kiểm tra Nội dung văn bản & Dữ liệu

  • expect(locator).to_have_text("Chuỗi văn bản"): Khớp chính xác 100% nội dung chuỗi (có phân biệt chữ hoa/thường).

  • expect(locator).to_contain_text("Từ khóa"): Chỉ cần chứa cụm từ mục tiêu (Rất khuyên dùng vì tính linh hoạt cao).

  • expect(locator).to_have_value("Giá trị nhập"): Sử dụng riêng cho các ô nhập liệu (<input>) để kiểm tra chuỗi chữ đang hiển thị bên trong.

    .to_have_text() / .to_contain_text()

    • Ví dụ Automation:

    error_message = page.locator(".error-text")
    
    # Kiểm tra chính xác
    expect(error_message).to_have_text("Username is required.")
    
    # Kiểm tra một phần (linh hoạt hơn)
    expect(error_message).to_contain_text("is required")

 

🌐 3. Kiểm tra cấp độ Toàn Trang (Page Level)

Thay vì kiểm tra một phần tử nhỏ, nhóm này dùng để kiểm tra trạng thái tổng quát của trình duyệt.

expect(page).to_have_url("https://the-internet.herokuapp.com/secure") # Kiểm tra chuyển hướng URL thành công
expect(page).to_have_title("Trang Chủ Hệ Thống")                       # Kiểm tra tiêu đề Tab trình duyệt

 


🔢 4. Kiểm tra Thuộc tính & Số lượng

  • expect(locator).to_have_attribute("tên_thuộc_tính", "giá_trị"): Kiểm tra các thuộc tính HTML như href, src, placeholder, class...

    • Ví dụ Automation:

      profile_link = page.locator("a.profile")
      expect(profile_link).to_have_attribute("href", "/user/profile")

  • expect(locator).to_have_count(số_lượng): Kiểm tra số lượng phần tử trả về (Ví dụ: Danh sách tìm kiếm phải trả về đúng 10 sản phẩm).

    • Ví dụ Automation:
      # Kiểm tra xem có đúng 6 sản phẩm trên trang
      products = page.locator(".inventory_item")
      expect(products).to_have_count(6)​

🔄 5. Assertions Phủ Định (Negative Assertions)

Để kiểm tra điều kiện ngược lại, bạn chỉ cần chèn từ khóa .not_ ngay trước hàm kiểm định:

expect(page.locator("#btn-submit")).not_to_be_disabled() # Xác minh nút ĐÃ ĐƯỢC BẬT (không còn bị khóa)
expect(page.locator(".error-banner")).not_to_be_visible() # Xác minh banner lỗi KHÔNG ĐƯỢC hiển thị

 

 

Ví dụ tổng hợp:

from playwright.sync_api import Page, expect

def test_assertions_ultimate_cheat_sheet(page: Page):
    """
    TỔNG HỢP TOÀN BỘ KHO VŨ KHÍ ASSERTIONS (EXPECT) TRONG PLAYWRIGHT PYTHON
    Học viên có thể tra cứu nhanh file này khi viết kịch bản kiểm thử.
    """
    
    # 🌐 1. Kiểm tra cấp độ Toàn Trang (Page Level Assertions)
    # Xác thực xem trình duyệt đã chuyển hướng đến đúng URL mục tiêu hay chưa
    expect(page).to_have_url("https://the-internet.herokuapp.com/secure") 
    # Kiểm tra tiêu đề (Title) hiển thị trên Tab của trình duyệt
    expect(page).to_have_title("Trang Chủ Hệ Thống - Dashboard")


    # 👁️ 2. Kiểm tra trạng thái hiển thị (Visibility)
    # Phần tử bắt buộc phải hiển thị rõ ràng trên màn hình giao diện
    expect(page.locator(".success-banner")).to_be_visible()
    # Phần tử phải bị ẩn đi hoặc biến mất hoàn toàn khỏi cấu trúc DOM (VD: Vòng xoay Loading)
    expect(page.locator(".loading-icon")).to_be_hidden()


    # 🔤 3. Kiểm tra nội dung văn bản & Dữ liệu nhập (Text & Value)
    # Khớp chính xác 100% nội dung chuỗi văn bản (Có phân biệt hoa/thường)
    expect(page.locator(".error-msg")).to_have_text("Lỗi đăng nhập.") 
    # Khớp một phần (Chỉ cần đoạn văn bản chứa từ khóa mục tiêu - Khuyên dùng)
    expect(page.locator(".welcome")).to_contain_text("Chào mừng") 
    # Kiểm tra chuỗi ký tự đang hiển thị bên trong ô nhập liệu (Dành riêng cho thẻ <input>)
    expect(page.locator("input#username")).to_have_value("lan_bat_bo")


    # 🟢 4. Kiểm tra trạng thái tương tác (Enabled / Disabled)
    # Kiểm tra phần tử bị mờ/khóa, người dùng không thể bấm hay điền dữ liệu
    expect(page.get_by_role("button", name="Gửi")).to_be_disabled()
    # Kiểm tra phần tử đã được mở khóa thành công, sẵn sàng nhận tương tác
    expect(page.get_by_role("button", name="Gửi")).to_be_enabled()


    # 🔗 5. Kiểm tra thuộc tính HTML (Attributes)
    # Xác minh giá trị của một thuộc tính bất kỳ (href, src, placeholder, class,...)
    expect(page.locator("a#profile")).to_have_attribute("href", "/profile")
    expect(page.locator("input#email")).to_have_attribute("placeholder", "Nhập email của bạn")


    # 🔢 6. Kiểm tra số lượng phần tử (Count)
    # Xác thực tổng số lượng phần tử tìm thấy trên giao diện phải khớp con số mong muốn
    expect(page.locator(".product-item")).to_have_count(12)


    # 🔄 7. Assertions Phủ Định (Negative Assertions - Sử dụng mạch lộn ngược với .not_)
    # Xác minh nút ĐÃ ĐƯỢC BẬT (Bản chất: Không ở trạng thái bị khóa)
    expect(page.locator("#btn-submit")).not_to_be_disabled()
    # Xác minh hệ thống hoạt động đúng và KHÔNG hiển thị banner thông báo lỗi
    expect(page.locator(".error-banner")).not_to_be_visible()


    # ⏱️ 8. Tùy chỉnh thời gian chờ (Custom Timeout)
    # Thay vì chờ 5 giây mặc định, tăng thời gian lên 12 giây (12000ms) cho các tác vụ nặng như xuất file
    expect(page.locator(".download-complete-toast")).to_be_visible(timeout=12000)


    # ☁️ 9. Kiểm định mềm (Soft Assertions - Tránh gãy luồng test nửa chừng)
    # Nếu logo bị thiếu, lỗi sẽ được ghi nhận vào báo cáo nhưng kịch bản vẫn tiếp tục chạy tiếp dòng dưới
    expect.soft(page.locator("#company-logo")).to_be_visible()
    
    # Luồng test vẫn chạy tiếp tục hành động điền form bình thường dù lệnh soft check ở trên có fail hay pass
    page.locator("#username").fill("admin")
    page.locator("#password").fill("secret_pass")


🪂 IV. Kỹ Thuật Assertions Nâng Cao


1. Tùy chỉnh thời gian chờ (Custom Timeout)

Nếu bạn biết chắc một tác vụ xử lý rất nặng (Ví dụ: Hệ thống cần 8 giây để xuất file Excel báo cáo), hãy tăng thời gian chờ riêng cho lệnh Assert đó để tránh bị lỗi timeout mặc định (5s).

# Kiên nhẫn chờ đợi thông báo thành công xuất hiện trong tối đa 12 giây
expect(page.locator(".download-complete-toast")).to_be_visible(timeout=12000)

2. Soft Assertions (Kiểm định mềm) ☁️

  • Vấn đề: Mặc định, nếu một lệnh expect thất bại (Fail), bài test sẽ dừng lại ngay lập tức tại đó.

  • Giải pháp: Sử dụng expect.soft(). Nếu lệnh này bị Fail, Playwright vẫn ghi nhận lỗi vào Report nhưng cho phép Test Case tiếp tục chạy tiếp các dòng lệnh phía sau.

  • Tình huống sử dụng: Cực kỳ hữu ích khi bạn cần kiểm tra một Checklist giao diện (Ví dụ: Xác minh xem trang web có đủ Logo, đúng màu sắc Banner, đúng Footer hay không) mà không muốn một lỗi nhỏ làm gián đoạn toàn bộ luồng kiểm thử chính.

    # Nếu thiếu logo, test case vẫn chạy tiếp để điền form phía dưới
    expect.soft(page.locator("#company-logo")).to_be_visible()
    
    page.locator("#username").fill("lan_bat_bo")
    page.locator("#password").fill("qa_automation_2026")
    page.locator("#login-click").click()

     


🧩 V. Bài Tập Thực Hành Tổng Hợp (Chuẩn AAA)

Kịch bản: Tương tác nâng cao trên trang tài nguyên khóa học

Hãy tạo tệp tin test_lesson5_assertions.py. Chúng ta sẽ áp dụng các hành động của Bài 4 kết hợp với kỹ thuật kiểm định của Bài 5 để giải quyết các bài toán giao diện động.

⏳ Kịch bản 1: Đánh bại giao diện tải chậm (Dynamic Loading)

  • Arrange: Truy cập hệ thống [https://the-internet.herokuapp.com/dynamic_loading/1](https://the-internet.herokuapp.com/dynamic_loading/1)

  • Act: Tìm và nhấn nút "Start". Lúc này một thanh Loading dạng xoay sẽ xuất hiện và chạy trong khoảng 5 giây.

  • Assert: Dùng expect kiểm tra dòng chữ Hello World! phải hiển thị trên màn hình một cách chính xác.

☑️ Kịch bản 2: Xác thực trạng thái Checkbox

  • Arrange: Truy cập hệ thống [https://the-internet.herokuapp.com/checkboxes](https://the-internet.herokuapp.com/checkboxes)

  • Act: Tiến hành tích chọn Checkbox số 1 và bỏ chọn Checkbox số 2.

  • Assert: Sử dụng .to_be_checked() và dạng phủ định để xác minh trạng thái của cả hai checkbox sau khi tương tá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