NỘI DUNG BÀI HỌC
✅ Củng cố mô hình Arrange – Act – Assert (AAA) và sử dụng expect trong Playwright.
✅ Thành thạo Fixtures trong pytest (cơ bản, nâng cao, nhiều scope) để tái sử dụng code setup & quản lý môi trường test.
✅ Hiểu và áp dụng Multi Tabs: mở tab mới từ click, chờ tab mới bằng context.expect_page, xây BasePage hỗ trợ.
✅ Làm chủ thao tác người dùng nâng cao:
-
Upload File với
set_input_files() -
Keyboard Actions với
page.keyboard.press()
✅ Hoàn thiện kịch bản E2E tổng hợp trên hệ thống HRM kết hợp AAA + Fixtures + Multi Tabs + Upload + Keyboard.
1️⃣ MÔ HÌNH AAA & EXPECT – VIẾT TEST CÓ TƯ DUY
1.1. Khái niệm AAA
Trong mọi test, luôn cố gắng chia rõ 3 phần:
-
Arrange (Chuẩn bị)
Chuẩn bị mọi thứ cần thiết trước khi hành động:-
Mở trang
-
Login
-
Tạo dữ liệu
-
Khởi tạo Page Object / Fixture
-
-
Act (Hành động)
Thao tác chính mà ta muốn kiểm thử:-
Click nút
-
Nhập form
-
Chọn dropdown
-
Upload file
-
-
Assert (Kiểm chứng)
Kiểm tra kết quả, dùngexpecttrong Playwright:-
expect(page).to_have_url(...) -
expect(locator).to_be_visible() -
expect(locator).to_have_text(...)
-
Lý do dùng AAA:
-
Code test dễ đọc: nhìn vào biết test đang làm gì.
-
Dễ review: biết chỗ nào là setup, chỗ nào là logic chính.
-
Dễ debug: nếu lỗi, biết lỗi ở Arrange/Act/Assert.
1.2. Ví dụ: Login thành công (AAA + expect)
from playwright.sync_api import Page, expect
def test_login_success(page: Page):
# ARRANGE – Chuẩn bị
page.goto("https://hrm.anhtester.com/erp/login")
# ACT – Hành động
page.fill("#iusername", "admin_example")
page.fill("#ipassword", "123456")
page.click("button[type='submit']")
# ASSERT – Kiểm chứng
expect(page).to_have_url("**/erp/dashboard")
expect(page.locator("h4.page-title")).to_have_text("Dashboard")
1.3. Bài tập 1 – AAA với login thất bại
Viết test test_login_fail_with_wrong_password theo AAA:
-
Arrange:
-
Mở trang login HRM
-
-
Act:
-
Nhập username đúng
-
Nhập password sai
-
Click Login
-
-
Assert:
-
Không chuyển sang
/dashboard -
Có hiển thị báo lỗi (VD: “Invalid credentials”)
-
Gợi ý cấu trúc:
2️⃣ FIXTURES – QUẢN LÝ MÔI TRƯỜNG & LOGIN DÙNG CHUNG
2.1. Fixtures là gì? Dùng để làm gì?
Trong pytest, fixture là nơi ta:
-
Setup môi trường test:
-
Mở browser, mở page
-
Login
-
Tạo dữ liệu test
-
-
(Tuỳ trường hợp) Teardown:
-
Xoá dữ liệu
-
Đóng browser / cleanup
-
Mục tiêu:
-
Không lặp đi lặp lại code login trong từng test.
-
Tách phần “Chuẩn bị” (Arrange) ra khỏi test → test ngắn & dễ hiểu.
2.2. Ví dụ: Fixture cho LoginPage
Giả sử ta đã có class LoginPage:
Tạo fixture trong conftest.py:
Dùng trong test:
👉 Ưu điểm:
-
Test không cần biết chi tiết locator
2.3. Fixture nâng cao – Dùng yield để setup + teardown
Khi cần vừa setup – vừa dọn dẹp, dùng yield:
Test có thể dùng temp_employee:
2.4. Scope fixture – function / module / session
-
function-
Mặc định
-
Mỗi test chạy lại fixture từ đầu
-
Cô lập, ít side-effect
-
-
module-
1 file test dùng chung 1 lần setup
-
Tiết kiệm thời gian khi nhiều test dùng chung login
-
-
session-
Toàn bộ test run dùng 1 lần fixture
-
Rất nhanh nhưng dễ bị ảnh hưởng lẫn nhau nếu code test chỉnh state quá nhiều
-
2.5. Ứng dụng: Fixture trả về DashboardPage đã login
Giờ test chỉ cần:
2.6. Bài tập 2 – Xây fixture login dùng chung
Yêu cầu:
-
Tạo fixture
dashboard_page:-
Mở trang login
-
Login bằng tài khoản
admin_example/123456 -
Khởi tạo
DashboardPage -
Gọi
dashboard.assert_is_current_page() -
Return
dashboard
-
-
Viết 2 test:
-
Test 1: Kiểm tra menu “PIM” hiển thị
-
Test 2: Kiểm tra menu “Time” hiển thị
-
Gợi ý:
Các method có thể đặt trong DashboardPage:
-
is_menu_visible("PIM") -
is_menu_visible("Time")
3️⃣ MULTI TABS – MỞ TAB MỚI TỪ CLICK
Phần này là ôn lại + nâng cấp từ giáo án trước:
Học viên nào bỏ lỡ buổi Multi Tabs sẽ được thấy ứng dụng thực tế trong buổi tổng hợp.
3.1. Khi nào cần xử lý Multi Tabs?
Một số tình huống:
-
Click vào link “Settings”, “Help”, “Policy” mở tab mới.
-
Link có
target="_blank". -
Website mở popup/tap mới bằng
window.open().
Nếu không xử lý đúng, test sẽ:
-
❌ Vẫn đứng ở tab cũ
-
❌ Không biết tab mới đã xuất hiện
-
❌ Click / assert ở “sai tab” → locator không tìm thấy
3.2. Nguyên tắc vàng: Lắng nghe trước – Click sau
Playwright cung cấp:
Quy tắc:
-
Gọi
context.expect_page()ở dạngwith→ chờ tab mới xuất hiện. -
Bên trong block đó mới
clickvào link mở tab. -
Sau khi ra khỏi
with, lấynew_page = new_page_info.value.
Ví dụ:
Nếu làm ngược:
-
Click xong
-
Rồi mới expect_page → lỡ mất event mở tab → code bị treo hoặc fail.
3.3. BasePage hỗ trợ Multi Tabs
Để tái sử dụng, ta viết helper trong BasePage:
3.4. DashboardPage mở Settings ở tab mới
3.5. SettingsPage trên tab mới
3.6. Ví dụ test Multi Tab đầy đủ
3.7. Bài tập 3 – Tự xử lý 1 tab mới
Yêu cầu:
-
Tìm hoặc mô phỏng 1 link trong HRM/website demo có mở tab mới.
-
Dùng helper
_click_and_wait_for_new_pagetrongBasePage. -
Viết test:
-
Đứng ở trang A (dashboard).
-
Click mở trang B (settings/help) trên tab mới.
-
Assert heading/URL tab B.
-
Đóng tab B.
-
Quay lại tab A → assert vẫn đang ở trang A.
-
4️⃣ UPLOAD FILE – set_input_files()
4.1. Khái niệm & Lưu ý
Upload file trong Playwright:
-
Không cần click nút “Choose file”.
-
Chỉ cần tìm đúng
<input type="file">. -
Dùng
locator.set_input_files("path/to/file").
Lưu ý:
-
Path file là path từ thư mục chạy pytest (thường là root project).
-
File upload nên để trong thư mục
data/hoặcresources/.
4.2. Ví dụ: Upload avatar trên trang Profile
Giả sử có ProfilePage:
Test:
4.3. Bài tập 4 – Upload logo công ty
Yêu cầu:
-
Tạo method
upload_company_logo(file_path)trongSettingsPage. -
Viết test:
-
Dùng
dashboard_page -
Mở
SettingsPage -
Upload logo
data/company_logo.png -
Assert:
-
Toast success hiển thị
-
Hoặc kiểm tra logo mới xuất hiện (tuỳ UI demo)
-
-
5️⃣ KEYBOARD ACTIONS – page.keyboard.press()
5.1. Khi nào cần dùng bàn phím?
-
Submit form bằng
Enter. -
Dùng phím mũi tên để chọn item trong dropdown.
-
Ctrl+A, Delete để xoá input.
5.2. Ví dụ: Submit login bằng phím Enter
from playwright.sync_api import Page, expect
def test_login_with_enter_key(page: Page):
# Arrange
page.goto("https://hrm.anhtester.com/erp/login")
page.fill("#iusername", "admin_example")
page.fill("#ipassword", "123456")
# Act – dùng phím Enter thay vì click
page.keyboard.press("Enter")
# Assert
expect(page).to_have_url("**/erp/dashboard")
5.3. Ví dụ: Xoá nội dung bằng Ctrl+A + Delete
5.4. Bài tập 5 – Keyboard thực tế
Yêu cầu:
-
Chọn 1 form có ô search hoặc ô text.
-
Flow:
-
Điền 1 chuỗi text
-
Dùng
Control+A+Deleteđể xoá -
Assert ô input rỗng
-
-
Sau đó, nhập text mới, bấm
Enterđể submit, assert chuyển trang hoặc hiển thị kết quả.
6️⃣ BÀI TẬP TỔNG HỢP CUỐI BUỔI (CHALLENGE E2E)
6.1. Mục tiêu
Viết 1 test E2E kết hợp toàn bộ kiến thức buổi 9:
-
AAA
-
Fixtures (dashboard_page)
-
Multi Tabs (Dashboard → Settings)
-
Upload File (logo hoặc avatar)
-
Keyboard (Enter / Ctrl+A + gõ lại)
6.2. Đề bài gợi ý
Scenario: Cập nhật thông tin công ty & logo qua trang Settings
-
Arrange:
-
Dùng fixture
dashboard_page(đã login). -
Mở
SettingsPagetrong tab mới (multi tab).
-
-
Act:
-
Trong
SettingsPage:-
Dùng Ctrl+A + Delete để xoá tên công ty cũ, nhập tên mới.
-
Upload logo mới bằng
set_input_files(). -
Bấm
Enter(hoặc click Save).
-
-
-
Assert:
-
Assert toast “Update successful”.
-
Đóng tab Settings.
-
Quay về Dashboard + assert vẫn đang ở Dashboard.
-
6.3. Gợi ý code cấu trúc
Trong SettingsPage, ta có thể viết:
7️⃣ TÓM TẮT KIẾN THỨC CỐT LÕI BUỔI 9
| Mảng | Cần hiểu | Công cụ / Kỹ thuật | Ý nghĩa |
|---|---|---|---|
| AAA | Chia test thành 3 bước | Arrange – Act – Assert | Test rõ ràng, dễ review |
| Fixtures | Chuẩn bị state dùng chung | @pytest.fixture, yield, scope |
Login 1 lần, đỡ lặp code |
| Multi Tabs | Tab mới là Page mới | context.expect_page(), helper _click_and_wait_for_new_page |
Điều khiển được tab mới |
| Upload File | Mô phỏng người dùng chọn file | locator.set_input_files() |
Test upload không phụ thuộc UI “Choose file” |
| Keyboard | Điều khiển bằng phím | page.keyboard.press() |
Xử lý Enter, Ctrl+A, Delete trong form |
