CYBERSOFT
Đăng nhập

Chống flaky ở quy mô lớn với Playwright

Chuyên nâng caoDịch vụ SaaSPlaywrightDebug/TraceNâng caoKinh nghiệm
🗓 1 tháng trước21 phút đọc·👁 918 lượt xem👤 33 người đọc

Coi flaky như bài toán vận hành: năm nhóm nguyên nhân gốc, web-first auto-waiting, bỏ sleep cứng, cô lập dữ liệu, retry + trace 'retain-on-failure-and-retries', quarantine có SLA, đo flake rate và chính sách cấp tổ chức. Kèm góc phỏng vấn.

1. Flaky là gì và vì sao nó giết niềm tin ở quy mô lớn

Một test flaky là test cho kết quả khác nhau trên cùng một commit mà không có thay đổi nào ở code sản phẩm: lúc xanh, lúc đỏ. Ở một dự án nhỏ mười test, một ca flaky chỉ gây khó chịu. Nhưng ở quy mô SaaS với năm nghìn test chạy trên hàng trăm pull request mỗi ngày, dù chỉ 1% flake rate cũng có nghĩa là gần như mọi lần chạy đều đỏ vì một lý do vô nghĩa. Đội ngũ mất niềm tin vào bộ test, bắt đầu bấm rerun theo phản xạ và cuối cùng bỏ qua cả những lần đỏ thật.

Bài viết này tiếp cận flaky như một bài toán vận hành ở quy mô tổ chức chứ không phải mẹo vặt lẻ tẻ. Chúng ta sẽ đi qua năm nhóm nguyên nhân gốc, cơ chế web-first auto-waiting của Playwright giúp diệt phần lớn chúng, chiến lược retry và trace đúng cách, cô lập test, quy trình quarantine có SLA, cách đo flake rate như một chỉ số sức khoẻ, và cuối cùng là chính sách cấp tổ chức để giữ tỉ lệ flaky luôn dưới ngưỡng. Mọi ví dụ đều xoay quanh nguyên tắc oracle: khẳng định bất biến nghiệp vụ, không khẳng định 'màn hình hiện ra'.

Định nghĩa dùng trong toàn bài: flake rate = số lần chạy đổi kết quả (pass↔fail) giữa các lần rerun / tổng số lần chạy, đo trên cùng commit.
5 NHÓM NGUYÊN NHÂN FLAKY / ROOT CAUSES Race / thời điểmDOM chưa sẵn sàng Animationtransition đang chạy Networkđộ trễ, retry API Thời gian / TZclock, timezone Dữ liệu testchung, không cô lập Oracle ổn định = web-first auto-waiting + cô lập dữ liệu expect(locator).toBeVisible() → chờ đúng điều kiện, KHÔNG sleep cứng mỗi test tự tạo/tự dọn state → chạy song song vẫn tất định
Bản đồ 5 nhóm nguyên nhân flaky và hai trụ chống flaky.

2. Năm nhóm nguyên nhân gốc của flaky

Trước khi chữa, phải phân loại. Gần như mọi ca flaky rơi vào năm nhóm. Nhóm race: test kiểm tra một phần tử trước khi ứng dụng render xong, hoặc bấm nút trước khi handler được gắn. Nhóm animation: một transition CSS làm phần tử dịch chuyển đúng lúc click, khiến toạ độ trượt. Nhóm network: API chậm bất thường, retry ngầm, hoặc thứ tự response không xác định. Nhóm thời gian: test phụ thuộc đồng hồ thật, timezone, hoặc mốc nửa đêm. Nhóm dữ liệu: nhiều test dùng chung một tài khoản, một bản ghi, và giẫm chân nhau khi chạy song song.

Bảng quyết định: triệu chứng → nhóm → cách chữa

  • Đỏ ngẫu nhiên ở 'element not found' → race → dùng locator + auto-wait, bỏ sleep.
  • Click trượt hoặc 'element is not stable' → animation → chờ animation xong hoặc tắt animation.
  • Timeout rải rác ở bước gọi API → network → mock hoặc route, tăng timeout đúng chỗ.
  • Đỏ chỉ vào buổi sáng hoặc cuối tháng → thời gian → freeze clock, cố định timezone.
  • Đỏ khi tăng workers, xanh khi chạy một mình → dữ liệu → cô lập state từng test.
💡 Khi phân loại, hãy hỏi 'điều gì thay đổi giữa hai lần chạy?'. Câu trả lời gần như luôn là một trong năm nhóm này, và nó chỉ thẳng cách chữa.

3. Web-first auto-waiting: vũ khí gốc chống race

Điểm khác biệt lớn nhất giữa Playwright và các công cụ đời cũ là cơ chế auto-waiting của web-first assertions. Khi bạn viết expect(locator).toBeVisible(), Playwright không chụp trạng thái một lần rồi so sánh; nó thăm dò lặp lại cho tới khi điều kiện đúng hoặc hết timeout. Tương tự, mỗi action như click sẽ tự chờ phần tử actionable: hiển thị, ổn định vị trí, bật, nhận được sự kiện và không bị phần tử khác che. Nhờ đó phần lớn race biến mất mà bạn không phải viết một dòng chờ nào.

ts
import { test, expect } from '@playwright/test';

test('giỏ hàng cập nhật tổng tiền — auto-wait làm việc thay ta', async ({ page }) => {
  await page.goto('/cart');
  // KHÔNG cần waitForSelector: click tự chờ nút actionable
  await page.getByRole('button', { name: 'Thêm gói Pro' }).click();

  // web-first assertion: thăm dò lặp tới khi tổng = 490.000đ hoặc hết timeout
  await expect(page.getByTestId('cart-total')).toHaveText('490.000₫');

  // oracle nghiệp vụ: số dòng hàng khớp, không âm
  await expect(page.getByRole('listitem')).toHaveCount(2);
});

Chìa khoá là phân biệt hai loại chờ. Chờ ngầm qua assertion và action là loại đúng: nó gắn với một điều kiện xác định và tự dừng ngay khi điều kiện thoả. Chờ tường minh bằng thời gian cố định là loại sai: nó luôn hoặc quá ngắn ở máy chậm, hoặc quá dài làm CI ì ạch. Nguyên tắc vàng: mọi lúc bạn định gõ sleep, hãy hỏi 'tôi đang chờ điều kiện gì?' rồi viết đúng assertion cho điều kiện đó.

Auto-waiting mặc định dùng ngưỡng timeout của expect (5 giây) và của action (30 giây). Chỉnh trong config, đừng rải magic number khắp test.
🔒

Nội dung bị khoá

Bạn đang xem bản xem trước. Đăng nhập để mở khoá toàn bộ bài viết và tất cả tài liệu Tester/QA.

Đã xem 24% nội dung
  • Đọc trọn vẹn mọi bài viết
  • Lưu bài & ghi chú cá nhân
  • Theo dõi tiến độ đã đọc
  • Luyện phỏng vấn, ISTQB, Mock
Đăng nhập để đọc tiếp

Chưa có mã? Lấy mã qua Fanpage / Zalo CyberSoft.

0

💬 Bình luận (0)

Bạn có thể đọc mọi bình luận. Đăng nhập để bình luận

Chưa có bình luận nào. Hãy là người đầu tiên!