Get in touch
or send us a question?
CONTACT

Học Playwright từ đầu — Bài học thật, thất bại thật

Tôi mất 3 tuần đầu học Playwright mà không chạy được một test nào hoàn chỉnh. Không phải vì Playwright khó — mà vì tôi đã học sai cách. Đây là những gì tôi ước mình biết sớm hơn.

Tại sao tôi chuyển từ Selenium sang Playwright?

Tháng 9 năm ngoái, team tôi quyết định migrate từ Selenium sang Playwright. Lý do rất đơn giản: mấy anh dev cứ than rằng test Selenium của bọn tôi “flaky” — nghĩa là hôm nay pass, ngày mai fail mà không rõ lý do. CI/CD pipeline trở thành nỗi ám ảnh. Deploy thứ Sáu là cả team run sợ.

Tôi được giao nhiệm vụ học Playwright và viết lại bộ test. Deadline: 6 tuần. Kinh nghiệm automation trước đó của tôi: 2 năm với Selenium, khá tự tin. Tôi nghĩ: “Playwright thì khác gì mấy đâu, chắc học nhanh thôi.

Tôi đã sai.

❌ Sai lầm #1

Tôi nhảy thẳng vào viết test mà không đọc docs. Cài xong, mở file example lên, thấy quen quen rồi bắt đầu code ngay. Kết quả: 3 ngày sau phải xóa hết và làm lại từ đầu.

Tuần 1: Cài đặt mà cũng fail

Bạn có thể nghĩ: “Cài đặt thì fail cái gì?” — Tôi cũng nghĩ vậy. Nhưng tôi đã dùng Node.js 14 trong khi Playwright yêu cầu Node 16+. Error message không hề nói rõ điều đó. Tôi ngồi Google hơn nửa ngày mới tìm ra.

# Lệnh tôi chạy (và ngồi nhìn error mãi)
npm init playwright@latest

# Error tôi nhận được:
Error: Cannot find module '../build/Release/native_messaging_host'

# Giải pháp: update Node.js lên v18 trước
nvm install 18
nvm use 18
💡 Bài học

Trước khi làm gì, kiểm tra phiên bản Node.js. Câu lệnh node --version mất 2 giây. Debug error mờ mắt mất nửa ngày.

Hành trình thật — tuần qua tuần

Tuần 1
Cài đặt thành công, viết test đầu tiên

Tìm hiểu cấu trúc thư mục, page object cơ bản. Hứng khởi.

Tuần 2
Bị ám bởi auto-waiting — cứ nghĩ phải thêm sleep()

Tư duy Selenium cũ khiến tôi thêm sleep() khắp nơi. Test chậm kinh khủng.

Tuần 3
Selector hell — locator nào đúng, nào sai?

getByRole vs getByTestId vs CSS selector. Tôi dùng tất cả lẫn lộn. Ai review code cũng lắc đầu.

Tuần 4
Hiểu ra triết lý của Playwright

Đọc lại docs từ đầu. Mọi thứ bắt đầu “click”.

Tuần 5–6
Viết lại toàn bộ test suite — sạch và stable

Pass CI/CD liên tục. Dev ngừng than phiền. Team hạnh phúc.

Sai lầm lớn nhất: mang tư duy Selenium vào Playwright

Đây là điều tôi muốn gào vào tai bất kỳ tester nào đang chuyển từ Selenium sang Playwright: hãy quên những gì bạn biết về wait.

Với Selenium, tôi quen thêm explicit wait hoặc sleep() để đợi element. Với Playwright, điều đó gần như không cần thiết vì Playwright có auto-waiting — nó tự chờ element ở trạng thái sẵn sàng trước khi tương tác.

// ❌ Tư duy Selenium cũ (tôi đã viết thế này)
await page.waitForTimeout(3000);
await page.click('#submit-btn');

// ✅ Playwright đúng cách — không cần chờ thủ công
await page.getByRole('button', { name: 'Đăng nhập' }).click();
// Playwright tự chờ button visible + enabled trước khi click
❌ Sai lầm #2

Test suite của tôi tuần 2 chạy mất 18 phút vì đầy waitForTimeout(). Sau khi xóa hết và tin tưởng vào auto-waiting, còn 6 phút. Cùng test coverage, cùng kết quả, nhanh gấp 3 lần.

Selector: tôi đã dùng sai loại trong 2 tuần

Playwright có nhiều cách để chọn element, nhưng chúng không bình đẳng. Tôi ban đầu dùng CSS selector vì quen — đó là lựa chọn kém bền nhất.

// Thứ tự ưu tiên selector (từ tốt → tệ)

// ✅ 1. Semantic — gắn với behavior của user
page.getByRole('button', { name: 'Xác nhận' })
page.getByLabel('Email')
page.getByPlaceholder('Nhập mật khẩu')

// ✅ 2. Test ID — ổn định, đặt riêng cho testing
page.getByTestId('login-btn')

// ⚠️ 3. Text — dễ thay đổi theo ngôn ngữ
page.getByText('Đăng nhập')

// ❌ 4. CSS/XPath — brittle, tôi đã dùng 2 tuần đầu
page.locator('.btn-primary > span:nth-child(2)')
💡 Bài học

Nếu test bạn hay fail khi dev thay đổi CSS hay HTML nhỏ — selector đang là vấn đề. Hãy ưu tiên getByRolegetByTestId. Nói chuyện với dev để họ thêm data-testid vào các element quan trọng.

Khoảnh khắc “à ra thế” — khi mọi thứ click vào nhau

Tuần 4, tôi ngồi đọc lại toàn bộ trang Best Practices của Playwright docs. Lần này không đọc để tìm cách fix bug — đọc để hiểu triết lý thiết kế. Và tôi nhận ra: Playwright được thiết kế để test từ góc nhìn của user, không phải từ góc nhìn của HTML.

Đó là bước ngoặt. Từ lúc đó, mỗi khi viết test, tôi tự hỏi: “User thực sự làm gì ở đây?” thay vì “Element nào mình cần click?

“Playwright được thiết kế để test từ góc nhìn của user — không phải từ góc nhìn của HTML. Câu hỏi không phải ‘element nào tôi click?’ mà là ‘user thực sự làm gì?'”

Kết quả sau 6 tuần

Bộ test mới với Playwright: 312 test cases, chạy song song trên 3 browser (Chrome, Firefox, Safari), thời gian CI xuống còn 8 phút từ 34 phút trước đó. Flaky test: từ 23% còn 1.2%.

✅ Kết quả thực tế

Lần đầu tiên trong lịch sử team, chúng tôi deploy thứ Sáu mà không ai run sợ. Dev không còn mở Slack lúc 6 giờ tối để hỏi “test có pass không?” Đó là cảm giác xứng đáng nhất sau 6 tuần vật lộn.

3 điều tôi khuyên nếu bạn bắt đầu hôm nay

Thứ nhất: đọc docs trước khi code. Tôi biết, tôi biết — nghe nhàm. Nhưng Playwright docs thực sự rất tốt và có nhiều ví dụ thực tế. Đặc biệt đọc phần Best PracticesAuto-waiting trước.

Thứ hai: dùng Playwright Inspector ngay từ đầu. Lệnh npx playwright test --debug mở ra một công cụ cực kỳ hữu ích để bạn thấy test đang chạy như thế nào, element nào được chọn, bước nào fail.

Thứ ba: đừng học một mình. Tôi đã mất 2 tuần lẩn quẩn vì ngại hỏi. Khi bắt đầu post câu hỏi lên Discord của Playwright và các nhóm tester Việt Nam, mọi thứ nhanh hơn nhiều. Cộng đồng rất nhiệt tình.

 

Sưu tầm

Bạn đang học Playwright và gặp khó khăn nào đó?
Comment bên dưới — tôi đọc và trả lời từng bình luận.