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:
-
📦 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ử.
-
🎬 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).
-
⚖️ 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)
- Ví dụ Automation:
🔄 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
expectthấ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
expectkiể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.
