1. Cách dùng bộ 50 câu hỏi này để phỏng vấn Playwright
Phỏng vấn một vị trí Automation với Playwright không phải là trò chơi đố mẹo cú pháp mà là cách người phỏng vấn đo tư duy kiểm thử của bạn. Bài viết này gom năm mươi câu hỏi thường gặp, chia theo ba cấp độ Junior, Mid và Senior, và với mỗi câu chúng tôi đưa câu trả lời mẫu, câu hỏi đào sâu tiếp theo, cùng phần 'điều gì làm câu trả lời được điểm cao'. Bạn nên đọc theo cấp độ mình đang ứng tuyển nhưng cũng nên lướt cấp cao hơn một bậc, vì người phỏng vấn giỏi luôn đẩy bạn ra khỏi vùng an toàn để xem trần năng lực.
Điểm chung của mọi câu trả lời tốt là gắn kỹ thuật vào bài toán nghiệp vụ. Khi được hỏi về locator, đừng chỉ liệt kê API; hãy nói vì sao getByRole giúp test bền hơn và gần với người dùng thật hơn. Khi được hỏi về mock, đừng chỉ khoe page.route; hãy nói bạn mock để cô lập lỗi flaky và để kiểm thử các nhánh lỗi mà backend khó tạo ra. Người phỏng vấn Senior đặc biệt thích ứng viên biết phân biệt 'test kiểm cái gì' với 'test chạy thế nào', và luôn quay về oracle: bất biến nghiệp vụ nào đang được bảo vệ.
2. Junior — Locator, auto-wait và triết lý 'user-first'
Ở cấp Junior, người phỏng vấn muốn chắc rằng bạn nắm nền tảng: chọn phần tử ra sao, vì sao Playwright ít flaky hơn Selenium đời cũ, và bạn có tư duy đứng ở góc người dùng hay không. Câu hỏi kinh điển nhất là về locator. Một ứng viên tốt sẽ ưu tiên locator theo vai trò (getByRole), theo nhãn (getByLabel) và theo text hiển thị, chỉ dùng test id khi không còn cách nào ngữ nghĩa. Lý do là locator ngữ nghĩa gắn với những gì người dùng thật nhìn thấy, nên khi lập trình viên đổi class CSS hay cấu trúc DOM, test không gãy vô cớ.
❓ Sự khác nhau giữa locator và ElementHandle là gì, và vì sao Playwright khuyên dùng locator?
ElementHandle chỉ trỏ tới một phần tử tại một thời điểm; nếu DOM render lại nó có thể 'stale'. Locator là một mô tả lười (lazy), chỉ tìm phần tử ngay lúc hành động chạy, nên tự động khớp với DOM mới nhất. Nhờ đó locator kèm auto-wait (tự chờ phần tử hiện, ổn định, có thể tương tác) và loại bỏ phần lớn sleep thủ công. Đây là lý do Playwright coi locator là API bậc nhất còn ElementHandle chỉ dùng trong trường hợp hiếm.
❓ Auto-wait của Playwright chờ những điều kiện gì trước khi click?
Trước khi click, Playwright chờ phần tử: gắn vào DOM, hiển thị (visible), ổn định (không còn animate/di chuyển), nhận được sự kiện (không bị phần tử khác che), và ở trạng thái enabled. Toàn bộ diễn ra trong ngưỡng timeout mặc định. Nhờ đó bạn hiếm khi phải viết waitForTimeout. Câu trả lời điểm cao còn nói thêm: nếu vẫn flaky thì nên soi bằng trace chứ đừng tăng sleep, vì sleep chỉ giấu triệu chứng.
import { test, expect } from '@playwright/test';
test('đăng nhập bằng locator ngữ nghĩa', async ({ page }) => {
await page.goto('/login');
// Ưu tiên vai trò + nhãn: bền khi CSS/DOM đổi
await page.getByLabel('Email').fill('qa@shop.io');
await page.getByLabel('Mật khẩu').fill('S3cure!');
await page.getByRole('button', { name: 'Đăng nhập' }).click();
// Web-first assertion: tự retry tới khi đúng hoặc timeout
await expect(page.getByRole('heading', { name: 'Bảng điều khiển' })).toBeVisible();
});3. Junior — Assertion, expect web-first và cấu hình cơ bản
Câu hỏi tiếp theo hay xoay quanh assertion. Nhiều ứng viên non kinh nghiệm dùng expect(await locator.textContent()).toBe(...), cách này chụp một khoảnh khắc và dễ flaky. Playwright khuyến khích assertion web-first như expect(locator).toHaveText(...), vì chúng tự động retry tới khi điều kiện đúng hoặc hết timeout. Người phỏng vấn muốn thấy bạn hiểu sự khác biệt giữa assertion 'chụp nhanh' và assertion 'tự chờ', vì đây là nguồn gốc phổ biến nhất của flaky ở đội mới.
❓ Vì sao nên tránh expect(await el.textContent()).toBe('X')?
Vì nó đọc giá trị đúng một lần rồi so sánh; nếu UI cập nhật bất đồng bộ (fetch, animation) thì giá trị lúc đọc chưa kịp đúng và test đỏ oan. Thay bằng expect(el).toHaveText('X') để Playwright tự thăm dò lại trong ngưỡng timeout. Trả lời điểm cao nói thêm: assertion web-first còn cho thông báo lỗi tốt hơn và tự chụp trace, giúp debug nhanh.
❓ Nêu vài trường quan trọng trong playwright.config.ts mà một Junior nên biết.
Các trường thường được hỏi: testDir, baseURL (để dùng đường dẫn tương đối), use.trace ('on-first-retry' hoặc 'retain-on-failure'), use.screenshot, retries, workers, projects (chạy nhiều trình duyệt), và webServer để tự khởi động app. Trả lời điểm cao: giải thích retries giúp gom flaky nhưng không được dùng để che lỗi thật, và trace là công cụ debug số một.
// playwright.config.ts — cấu hình tối thiểu một Junior nên đọc hiểu
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 4 : undefined,
reporter: [['html'], ['list']],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry', // ghi trace khi retry -> debug flaky
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
webServer: { command: 'npm run start', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI },
});
💬 Bình luận (0)
Chưa có bình luận nào. Hãy là người đầu tiên!