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>quafor/id, hoặcaria-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.waitForFunctionpoll 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ìmname: 'Đóng', không phải'×'. - getByText hay match thừa: text "Xoá" match cả "Xoá tất cả" và "Đã xoá". Cần
exact: truehoặ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; asserttoBeVisible()để 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).
