Visual Testing và Screenshot Comparison
Test chức năng kiểm tra “nút có hoạt động không”, nhưng không bắt được “nút bị lệch, đổi màu, hay biến mất khỏi layout”. Visual regression testing lấp khoảng trống đó: chụp ảnh giao diện, so với ảnh chuẩn (baseline), báo động khi có sai khác pixel. Playwright tích hợp sẵn tính năng này.
toHaveScreenshot — so sánh ảnh
test('giao diện trang chủ không đổi', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png');
});Lần chạy đầu tiên: chưa có baseline → Playwright chụp và lưu, test được đánh dấu “đã tạo snapshot” (coi như chưa pass thật). Các lần sau: chụp ảnh mới, so với baseline. Lệch quá ngưỡng → fail kèm ảnh diff.
Ảnh baseline lưu cạnh file test:
tests/
home.spec.ts
home.spec.ts-snapshots/
homepage-chromium-darwin.png ← baseline theo project + OSTên baseline gắn với trình duyệt và hệ điều hành. Đây là lý do visual test phải tạo baseline trên cùng môi trường sẽ chạy (thường là CI/Docker), không phải máy local — sẽ nói kỹ ở phần CI.
Chụp một phần tử thay vì cả trang
So cả trang dễ vỡ vì nhiều thứ thay đổi. Khoanh vùng vào component cần kiểm soát:
await expect(page.getByRole('navigation')).toHaveScreenshot('nav.png');
await expect(page.getByTestId('product-card')).toHaveScreenshot('card.png');Cấu hình ngưỡng sai khác
Khác biệt render nhỏ (anti-aliasing, font hinting) là bình thường. Điều chỉnh độ nhạy:
await expect(page).toHaveScreenshot('home.png', {
maxDiffPixels: 100, // cho phép tối đa 100 pixel khác
maxDiffPixelRatio: 0.01, // hoặc 1% tổng số pixel
threshold: 0.2, // ngưỡng khác biệt mỗi pixel (0-1), mặc định 0.2
});Đặt mặc định toàn dự án trong config:
// playwright.config.ts
expect: {
toHaveScreenshot: { maxDiffPixelRatio: 0.01, threshold: 0.2 },
},Xử lý nội dung động — kẻ thù của visual test
Ngày giờ, avatar ngẫu nhiên, quảng cáo, animation… khiến ảnh khác nhau mỗi lần chạy. Cách xử lý:
await expect(page).toHaveScreenshot('home.png', {
// Che các vùng động — Playwright phủ ô màu lên trước khi so
mask: [page.getByTestId('timestamp'), page.locator('.ad-banner')],
// Tắt animation để ảnh ổn định
animations: 'disabled',
// Chụp full page
fullPage: true,
});Các mẹo bổ sung làm ổn định:
// Cố định thời gian/seed nếu app cho phép
await page.clock.setFixedTime(new Date('2026-06-30T08:00:00'));
// Ẩn caret nhấp nháy, vô hiệu hoá transition qua CSS inj, v.v.
await expect(page).toHaveScreenshot({ caret: 'hide' });Cập nhật baseline khi thay đổi là chủ đích
Khi bạn cố ý đổi UI, cập nhật ảnh chuẩn:
# Cập nhật toàn bộ snapshot
npx playwright test --update-snapshots
# Chỉ một file
npx playwright test home.spec.ts --update-snapshotsSau đó review ảnh mới trong code review như review code — đây là bước quan trọng để không vô tình “đóng dấu” một lỗi giao diện.
So sánh ảnh bất kỳ (không phải snapshot trang)
toMatchSnapshot so một buffer/chuỗi bất kỳ với baseline — hữu ích cho ảnh tải về, file export:
const download = await downloadPromise;
const buffer = await fs.readFile(await download.path());
expect(buffer).toMatchSnapshot('report-thumbnail.png');Giữ visual test ổn định trên CI
Đây là phần khiến nhiều người bỏ cuộc. Nguyên tắc:
- Tạo baseline trên CI/Docker, không trên máy local — render khác nhau giữa OS/máy.
- Dùng cùng image Docker (ví dụ
mcr.microsoft.com/playwright) cho cả tạo baseline lẫn chạy CI. - Ưu tiên chụp component thay vì full page để giảm điểm vỡ.
- Luôn
animations: 'disabled'vàmaskvùng động. - Đặt ngưỡng
maxDiffPixelRatiohợp lý — quá chặt thì flaky, quá lỏng thì bỏ sót lỗi.
Khi nào dùng — và không dùng — visual test
- Nên: design system, component library, trang marketing tĩnh, layout quan trọng.
- Cân nhắc: trang nhiều nội dung động, dashboard real-time — chi phí duy trì baseline cao.
- Visual test bổ sung chứ không thay thế test chức năng. Một ảnh “khớp pixel” không đảm bảo logic đúng.
Tóm tắt
toHaveScreenshottạo baseline lần đầu, các lần sau so pixel và fail nếu lệch quá ngưỡng.- Ưu tiên chụp component, dùng
mask+animations: 'disabled'để chống nội dung động gây flaky. - Baseline gắn với OS + trình duyệt → tạo và chạy trên cùng môi trường (Docker/CI).
- Cập nhật bằng
--update-snapshotsvà review ảnh mới như review code; visual test bổ sung, không thay thế test chức năng.
Bài trước: ← API Testing với Playwright
Bài tiếp theo: Debugging: Trace Viewer, Codegen và UI Mode →