Wiki

← Tất cả bài wiki

getBy* Locators (Bộ locator theo ngữ nghĩa người dùng)

automationplaywright

getBy* là bộ locator ưu tiên số 1 của Playwright: getByRole, getByText, getByLabel, getByPlaceholder, getByTestId, getByAltText, getByTitle. Chúng tìm phần tử theo cách NGƯỜI DÙNG nhìn UI (role accessibility, chữ hiển thị, label...) thay vì cấu trúc HTML — vừa ổn định trước refactor, vừa ép app đúng chuẩn a11y.

Khái niệm & Bản chất

  • Triết lý user-facing: getBy* mô phỏng cách người dùng (và screen reader) xác định phần tử — nhìn nút theo chữ trên nút, ô nhập theo label bên cạnh, ảnh theo mô tả. Đổi class/CSS/markup wrapper không ảnh hưởng test.
  • getByRole là vua: tìm theo ARIA role ngầm (button, link, textbox, checkbox, heading...) + tên accessible (accessible name). Playwright docs khuyên dùng đầu tiên vì nó cưỡng chế theo cấu trúc accessibility thật.
  • Khác getByText: getByText match theo nội dung chữ hiển thị (substring mặc định, không phân biệt hoa thường); getByRole match theo role+name nên chính xác và ít false-positive hơn.
  • Sản phẩm của a11y tree: getByRole/getByLabel đọc Accessibility Tree của browser — nếu app của bạn không dùng semantic HTML đúng, locator sẽ không thấy element → phản hồi sớm về chất lượng markup.

Cách hoạt động & API chính

  • getByRole(role, { name, ... }): role là tên ARIA ('button', 'link', 'textbox', 'heading', 'combobox', 'checkbox'...). Option hay dùng: name (accessible name, có thể regex), exact: true, level (cho heading), checked, pressed, expanded.
  • getByText(text, { exact }): match phần tử chứa text. Mặc định substring + case-insensitive + normalize whitespace; exact: true để match nguyên văn.
  • getByLabel(text): input/textarea được gắn với <label> qua for/id, hoặc aria-label/aria-labelledby. Đây là cách đúng nhất để lấy ô form.
  • getByPlaceholder / getByAltText / getByTitle: map trực tiếp 3 attribute HTML. Dùng khi form không có label (placeholder) hoặc ảnh/icon.
  • getByTestId: tìm theo attribute test (mặc định data-testid, đổi được qua config). Đây là phao cứu sinh khi UI không có chữ/role ổn định.
  • Thứ tự ưu tiên thực chiến: getByRole → getByLabel → getByPlaceholder → getByText → getByTestId → CSS/XPath là cuối cùng.

Ví dụ & Cú pháp thực tế

  • Các getBy phổ biến: ``ts import { test, expect } from '@playwright/test'; test('đăng nhập với getBy* locators', async ({ page }) => { await page.goto('/login'); // label "Email" gắn với input qua for/id await page.getByLabel('Email').fill('student@hoctest.com'); await page.getByLabel('Mật khẩu').fill('secret'); // nút theo role + tên hiển thị await page.getByRole('button', { name: 'Đăng nhập' }).click(); // heading để xác nhận đã vào trang chủ await expect(page.getByRole('heading', { name: 'Trang chủ', level: 1 })).toBeVisible(); }); ``
  • getByText và exact: ``ts // match substring, không phân biệt hoa/thường await page.getByText('đơn hàng').click(); // match chính xác nguyên cụm await page.getByText('Xác nhận', { exact: true }).click(); // cũng dùng được regex await expect(page.getByText(/tổng cộng: \d+ đ/)).toBeVisible(); ``
  • Khi không có label/role tốt — dùng testId: ``ts // icon Xoá không có chữ — đặt data-testid ở app await page.getByTestId('delete-item-42').click(); // vùng chart, canvas — không nằm trong a11y tree await page.getByTestId('sales-chart').screenshot(); ``
  • Lọc role có trạng thái: page.getByRole('checkbox', { name: 'Nhớ tôi', checked: false }), page.getByRole('button', { name: 'Menu', expanded: true }) — cực mạnh với component phức tạp.

Best practices & Khi nào dùng

  • Mặc định mở đầu bằng getByRole: chỉ xuống getByText/CSS khi role không đủ phân biệt (vd: 2 nút cùng tên ở 2 vùng khác nhau — khi đó chain locator theo vùng chứ không đổi sang class).
  • Với form: luôn getByLabel trước, placeholder sau — label là contract dài hạn; placeholder có thể đổi theo marketing copy.
  • Chuẩn hoá ngôn ngữ trong test: quyết định test viết theo tiếng Việt hiển thị hay tiếng Anh của app, đừng lẫn — nếu app đa ngôn ngữ thì ưu tiên role/testid hơn text.
  • Codegen tự sinh getBy*: chạy npx playwright codegen <url> — nó ưu tiên pick getByRole; học cách nó chọn để rèn gu selector.
  • Dùng getByRole làm cách kiểm a11y miễn phí: nếu locator không tìm được nút vì dev dùng <div onclick>, đó là bug a11y thật — report chứ đừng lách bằng CSS.

Gotchas, So sánh & Lưu ý

  • getByRole chậm hơn CSS 1 chút vì phải dựng a11y tree — không đáng lo trong test, nhưng đừng lạm dụng trong page.waitForFunction poll dày đặc.
  • name không phải innerText: accessible name gộp label/alt/aria-label tuỳ loại element. Nút icon <button aria-label="Đóng">×</button> phải tìm name: 'Đóng', không phải '×'.
  • getByText hay match thừa: text "Xoá" match cả "Xoá tất cả" và "Đã xoá". Cần exact: true hoặc chuyển sang getByRole có name rõ ràng.
  • Element ẩn vẫn được match: getBy* không lọc visibility — locator có thể trỏ vào element display:none; assert toBeVisible() để chắc chắn, hoặc action sẽ tự fail khi timeout.
  • So sánh Testing Library: cùng triết lý "query theo user", nhưng Playwright mạnh hơn vì có auto-wait + strict mode + nhiều option lọc trạng thái hơn (checked, pressed, level).