Non-retrying Assertions (expect(value) kiểu Jest — assert giá trị đã lấy ra)
automationplaywright
Non-retrying assertion là expect kiểu Jest truyền thống: expect(value).toBe(...) kiểm tra một giá trị JavaScript đã có sẵn (số, chuỗi, object, boolean) đúng một lần, không poll, không chờ. Dùng khi assert dữ liệu đã kéo ra ngoài DOM — kết quả API, giá trị count, object parse được — còn mọi kiểm tra trạng thái UI thì phải dùng web-first locator assertion.
Khái niệm & Bản chất
- Một nhát, không chờ:
expect(count).toBe(5)so sánhcounttại đúng thời điểm dòng lệnh chạy — không có retry. Giá trị sau đổi thì assertion này vẫn giữ kết quả đã đánh giá. - Cùng 'họ' Jest matchers: toàn bộ core matchers Jest dùng được trong
@playwright/test—toBe,toEqual,toContain,toBeGreaterThan,toMatch,toBeNull,toBeTruthy,toHaveProperty,toThrow... - Khác biệt cốt lõi với web-first: web-first assert trạng thái tương lai của DOM ('sẽ hiển thị'), non-retrying assert giá trị hiện tại trong tay ('count lúc nãy là 5'). Chọn sai loại = flaky hoặc test pass ảo.
- Vai trò trong test Playwright: kiểm chứng dữ liệu logic — response API, số liệu tính toán, nội dung file download, giá trị JS trên
window, số lượng element đãcount()xong.
Cách hoạt động & API chính
- So sánh nguyên thủy:
toBe(===, so reference cho object),toEqual/toStrictEqual(deep equality cho object/array),toBeTruthy/toBeFalsy,toBeNull/toBeDefined/toBeUndefined/toBeNaN. - So sánh số:
toBeGreaterThan,toBeGreaterThanOrEqual,toBeLessThan,toBeCloseTo(n, digits)— số float bắt buộctoBeCloseTo, đừngtoBe(0.1 + 0.2). - Chuỗi & collection:
toContain(substring|item)(string hoặc array),toMatch(regex|string),toHaveLength(n),toContainEqual(obj)(deep-equal item trong array). - Object:
toHaveProperty('a.b', value)(nested path),toMatchObject(partial)(subset match — rất hay cho API response chỉ quan tâm vài field). - Negation:
.notcho mọi matcher:expect(list).not.toContain('admin'). VàtoThrowcho function phải throw:expect(() => JSON.parse(bad)).toThrow(). - Custom message:
expect(value, 'mô tả ngữ cảnh').toBe(x)— tham số thứ hai là message hiển thị khi fail, nên dùng cho assert logic khó đọc.
Ví dụ & Cú pháp thực tế
- ```ts import { test, expect } from '@playwright/test'; test('assert giá trị đã kéo ra', async ({ page }) => { await page.goto('/users'); // count() resolve ra số → non-retrying assertion const rowCount = await page.getByRole('row').count(); expect(rowCount, 'bảng phải có ít nhất 10 dòng data').toBeGreaterThan(10); // textContent đã lấy ra là string thường const title = await page.getByRole('heading').first().textContent(); expect(title).toContain('Danh sách'); expect(title).toMatch(/\d{4}/); // chứa năm });
- ```ts test('assert API response & object', async ({ request }) => { const res = await request.get('/api/orders/42'); expect(res.ok()).toBeTruthy(); expect(res.status()).toBe(200); const body = await res.json(); // Chỉ quan tâm vài field — toMatchObject expect(body).toMatchObject({ id: 42, status: 'paid', customer: { email: 'qa@hoctest.com' }, }); // Deep equality toàn bộ expect(body.items).toEqual([ expect.objectContaining({ sku: 'PW-101', qty: 2 }), ]); });
- ```ts test('assert giá trị trên window / computed', async ({ page }) => { await page.goto('/cart'); const total = await page.evaluate( () => (window as any).__cart?.total ?? 0 ); expect(total).toBeCloseTo(199.99, 2); // float: luôn dùng toBeCloseTo // Assert function throw expect(() => JSON.parse('not-json')).toThrow(SyntaxError); });
Best practices & Khi nào dùng
- Quy tắc chọn: kiểm tra thứ còn có thể thay đổi (DOM, URL, title) → web-first. Kiểm tra thứ đã chốt (response đã parse, số đã count, string đã lấy) → non-retrying.
- Dùng cho API test thuần:
requestfixture + expect kiểu Jest là combo chuẩn kiểm thử API trong Playwright — status, body schema, header đều là giá trị đã resolve. toMatchObjectcho API response: chỉ khai báo field quan tâm, API thêm field mới không làm vỡ test — tránhtoEqualtoàn bộ response trừ khi snapshot contract.- Kèm message cho assert logic:
expect(discount, 'giảm giá 10% cho đơn >500k').toBe(50_000)— khi fail đọc report hiểu ngay ngữ cảnh nghiệp vụ. - Assert số lượng sau khi UI ổn định:
await expect(rows).toHaveCount(n)trước, rồiexpect(await rows.count()).toBe(n)thừa — một cái là đủ; dùng non-retrying khi cần con số để tính tiếp (vdcount > 0để loop).
Gotchas, So sánh & Lưu ý
- Anti-pattern số 1:
expect(await locator.textContent()).toBe('Hello')— vừa mất auto-retry (textContent lấy snapshot tức thì) vừa match exact phá whitespace. Viết lạiawait expect(locator).toHaveText('Hello'). - Anti-pattern 2:
expect(await locator.isVisible()).toBeTruthy()—isVisible()check ngay, element render chậm 200ms là fail. Viết lạiawait expect(locator).toBeVisible(). toBevstoEqual:toBelà === — hai object nội dung giống hệt nhau vẫntoBefail vì khác reference. So nội dung object/array phảitoEqual.- Số float:
0.1 + 0.2 === 0.3là false — dùngtoBeCloseTo(0.3)thaytoBe. - Non-retrying trong vòng poll tự viết: nếu bạn tự loop check API, đừng assert cứng trong loop (fail là chết luôn lần đầu) — để
toPassbọc ngoài hoặc check điều kiện return, xem bài expect.configure & toPass. - Không await được — và cũng không cần:
expect(value).toBe()là đồng bộ; thêmawaitkhông hại nhưng thừa. Ngược lại web-first BẮT BUỘC await.
