https://playwright.dev/docs/test-assertions
Giới thiệu
- Playwright sử dụng
expect
để thực hiện assertion. - Sử dụng đơn giản:
expect(value)
và chọn một “matcher” theo mong muốn. VD:toEqual
,toContain
,toBeTruthy
expect(success).toBeTruthy();
- Ngoài ra thì Playwright cũng có các async matcher (bất đồng bộ), giúp kiểm tra các phần tử trên trang một cách hiệu quả hơn
await expect(page.getByTestId('status')).toHaveText('Submitted');
- Trong ví dụ trên thì Playwright sẽ tự test lại phần tử có test-id là “status” cho tới khi nó có text là “Submitted” thì thôi.
- Nó sẽ retry liên tục cho tới lúc timeout thì thôi.
- Có thể cài đặt thời gian timeout trong
testConfig.expect
.
Auto retry assertions
- Đây chính là các async matcher mà mình nói phía trên.
- Vì nó bất đồng bộ ~> bạn cần phải thêm
await
phía trước.
Assertion | Description |
---|---|
await expect(locator).toBeAttached() | Phần tử được attached vào DOM |
await expect(locator).toBeChecked() | Checkbox được checked |
await expect(locator).toBeDisabled() | Phần tử được disable |
await expect(locator).toBeEditable() | Phần tử editable |
await expect(locator).toBeEmpty() | Phần tử empty |
await expect(locator).toBeEnabled() | Phần tử enable |
await expect(locator).toBeFocused() | Phần tử focused |
await expect(locator).toBeHidden() | Phần tử không visible (bị ẩn đi) |
await expect(locator).toBeInViewport() | Phần tử nằm trong viewport |
await expect(locator).toBeVisible() | Phần tử hiển thị (trái ngược với toBeHidden) |
await expect(locator).toContainText() | Phần tử chứa text |
await expect(locator).toHaveAccessibleDescription() | Phần tử chứa accessible description |
await expect(locator).toHaveAccessibleName() | Phần tử chứa accessible name |
await expect(locator).toHaveAttribute() | Phần tử chứa DOM attribute |
await expect(locator).toHaveClass() | Phần tử chứa class |
await expect(locator).toHaveCount() | Phần tử chứa chính xác số lượng |
await expect(locator).toHaveCSS() | Phần tử chứa thuộc tính CSS |
await expect(locator).toHaveId() | Phần tử chứa id |
await expect(locator).toHaveJSProperty() | Phần tử chứa Javascript property |
await expect(locator).toHaveRole() | Element has a specific ARIA role |
await expect(locator).toHaveScreenshot() | Element has a screenshot |
await expect(locator).toHaveText() | Element matches text |
await expect(locator).toHaveValue() | Input has a value |
await expect(locator).toHaveValues() | Select has options selected |
await expect(page).toHaveScreenshot() | Page has a screenshot |
await expect(page).toHaveTitle() | Page has a title |
await expect(page).toHaveURL() | Page has a URL |
await expect(response).toBeOK() | Response has an OK status |
Non-retrying assertion
- Đây là những assertion sẽ kiểm tra ngay lập tức giá trị mà bạn mong muốn. Không có retry đâu.
- Thường thì nên dùng retrying assertion phía trên nhé anh em. Non-retry hay làm cho test flaky lắm.
- Toàn cái dễ nên mình để nguyên tiếng Anh đọc nhé anh em 😉
Assertion | Description |
---|---|
expect(value).toBe() | Value is the same |
expect(value).toBeCloseTo() | Number is approximately equal |
expect(value).toBeDefined() | Value is not undefined |
expect(value).toBeFalsy() | Value is falsy, e.g. false, 0, null, etc. |
expect(value).toBeGreaterThan() | Number is more than |
expect(value).toBeGreaterThanOrEqual() | Number is more than or equal |
expect(value).toBeInstanceOf() | Object is an instance of a class |
expect(value).toBeLessThan() | Number is less than |
expect(value).toBeLessThanOrEqual() | Number is less than or equal |
expect(value).toBeNaN() | Value is NaN |
expect(value).toBeNull() | Value is null |
expect(value).toBeTruthy() | Value is truthy, i.e. not false, 0, null, etc. |
expect(value).toBeUndefined() | Value is undefined |
expect(value).toContain() | String contains a substring |
expect(value).toContain() | Array or set contains an element |
expect(value).toContainEqual() | Array or set contains a similar element |
expect(value).toEqual() | Value is similar – deep equality and pattern matching |
expect(value).toHaveLength() | Array or string has length |
expect(value).toHaveProperty() | Object has a property |
expect(value).toMatch() | String matches a regular expression |
expect(value).toMatchObject() | Object contains specified properties |
expect(value).toStrictEqual() | Value is similar, including property types |
expect(value).toThrow() | Function throws an error |
expect(value).any() | Matches any instance of a class/primitive |
expect(value).anything() | Matches anything |
expect(value).arrayContaining() | Array contains specific elements |
expect(value).closeTo() | Number is approximately equal |
expect(value).objectContaining() | Object contains specific properties |
expect(value).stringContaining() | String contains a substring |
expect(value).stringMatching() | String matches a regular expression |
Negating matcher
- Hiểu đơn giản là làm cho nó kiểm tra ngược lại.
- Ví dụ: đang kiểm tra bằng (0), thêm not vào, thành kiểm tra khác 0
expect(value).not.toEqual(0);
await expect(locator).not.toContainText('some text');
Soft assertions
- Mặc định, nếu assertion fail sẽ dừng test lại.
- Playwright cung cấp khái niệm
soft assertion
, giúp test không dừng chạy khi fail. Và khi kết thúc test, vẫn sẽ đánh dấu test là fail.
// Make a few checks that will not stop the test when failed...
await expect.soft(page.getByTestId('status')).toHaveText('Success');
await expect.soft(page.getByTestId('eta')).toHaveText('1 day');
// ... and continue the test to check more things.
await page.getByRole('link', { name: 'next page' }).click();
await expect.soft(page.getByRole('heading', { name: 'Make another order' })).toBeVisible();
- Có thể kết hợp cả
soft assertion
vàassertion
// Make a few checks that will not stop the test when failed...
await expect.soft(page.getByTestId('status')).toHaveText('Success');
await expect.soft(page.getByTestId('eta')).toHaveText('1 day');
// Avoid running further if there were soft assertion failures.
expect(test.info().errors).toHaveLength(0);
Custom expect message
- Tức là bạn thay đổi cái message hiển thị của Playwright lúc chạy test.
- Nếu pass hay fail thì nó sẽ hiện message này.
- Ví dụ:
await expect(page.getByText('Name'), 'should be logged in').toBeVisible();
- Nếu chạy pass thì sẽ hiển thị như sau:
✅ should be logged in @example.spec.ts:18
- Nếu chạy fail thì sẽ hiển thị như sau:
Error: should be logged in
Call log:
- expect.toBeVisible with timeout 5000ms
- waiting for "getByText('Name')"
2 |
3 | test('example test', async({ page }) => {
> 4 | await expect(page.getByText('Name'), 'should be logged in').toBeVisible();
| ^
5 | });
6 |
- Có thể config custom expect message cho cả soft assertion nha
expect.soft(value, 'my soft assertion').toBe(56);
expect.configure
- Có thể tạo ra các expect theo mong muốn
const slowExpect = expect.configure({ timeout: 10000 });
await slowExpect(locator).toHaveText('Submit');
// Always do soft assertions.
const softExpect = expect.configure({ soft: true });
await softExpect(locator).toHaveText('Submit');
expect.poll
- Dùng để convert một expect thông thường thành async expect sử dụng hàm
expect.poll
:
await expect.poll(async () => {
const response = await page.request.get('https://api.example.com');
return response.status();
}, {
// Custom expect message for reporting, optional.
message: 'make sure API eventually succeeds',
// Poll for 10 seconds; defaults to 5 seconds. Pass 0 to disable timeout.
timeout: 10000,
}).toBe(200);
- Trong ví dụ trên, hàm poll sẽ gọi lại cho tới khi giá trị giống như trong
toBe
thì thôi. - Bạn có thể setting để cho gọi lại theo thời gian mong muốn
await expect.poll(async () => {
const response = await page.request.get('https://api.example.com');
return response.status();
}, {
// Probe, wait 1s, probe, wait 2s, probe, wait 10s, probe, wait 10s, probe
// ... Defaults to [100, 250, 500, 1000].
intervals: [1_000, 2_000, 10_000],
timeout: 60_000
}).toBe(200);
expect.toPass
- Bạn có thể retry một đoạn code cho tới khi nó success:
await expect(async () => {
const response = await page.request.get('https://api.example.com');
expect(response.status()).toBe(200);
}).toPass();
- Bạn cũng có thể thay đổi timeout và interval giống expect.poll vậy
await expect(async () => {
const response = await page.request.get('https://api.example.com');
expect(response.status()).toBe(200);
}).toPass({
// Probe, wait 1s, probe, wait 2s, probe, wait 10s, probe, wait 10s, probe
// ... Defaults to [100, 250, 500, 1000].
intervals: [1_000, 2_000, 10_000],
timeout: 60_000
});
Tạo ra một matcher riêng theo ý muốn
- Khá đơn giản, bạn chỉ cân extend expect là được
import { expect as baseExpect } from '@playwright/test';
import type { Page, Locator } from '@playwright/test';
export { test } from '@playwright/test';
export const expect = baseExpect.extend({
async toHaveAmount(locator: Locator, expected: number, options?: { timeout?: number }) {
const assertionName = 'toHaveAmount';
let pass: boolean;
let matcherResult: any;
try {
await baseExpect(locator).toHaveAttribute('data-amount', String(expected), options);
pass = true;
} catch (e: any) {
matcherResult = e.matcherResult;
pass = false;
}
const message = pass
? () => this.utils.matcherHint(assertionName, undefined, undefined, { isNot: this.isNot }) +
'\n\n' +
`Locator: ${locator}\n` +
`Expected: ${this.isNot ? 'not' : ''}${this.utils.printExpected(expected)}\n` +
(matcherResult ? `Received: ${this.utils.printReceived(matcherResult.actual)}` : '')
: () => this.utils.matcherHint(assertionName, undefined, undefined, { isNot: this.isNot }) +
'\n\n' +
`Locator: ${locator}\n` +
`Expected: ${this.utils.printExpected(expected)}\n` +
(matcherResult ? `Received: ${this.utils.printReceived(matcherResult.actual)}` : '');
return {
message,
pass,
name: assertionName,
expected,
actual: matcherResult?.actual,
};
},
});
- Sử dụng trong code
import { test, expect } from './fixtures';
test('amount', async () => {
await expect(page.locator('.cart')).toHaveAmount(4);
});
-
** Lưu ý **: không được nhầm lẫn giữa
Playwright expect
vàthư viện expect
nhé bạn. Chú ý để ý import không lại nhầm. -
Gộp matcher từ nhiều modules khác nhau
// fixture.ts
import { mergeTests, mergeExpects } from '@playwright/test';
import { test as dbTest, expect as dbExpect } from 'database-test-utils';
import { test as a11yTest, expect as a11yExpect } from 'a11y-test-utils';
export const expect = mergeExpects(dbExpect, a11yExpect);
export const test = mergeTests(dbTest, a11yTest);
- Trong file test
import { test, expect } from './fixtures';
test('passes', async ({ database }) => {
await expect(database).toHaveDatabaseUser('admin');
});
Trả lời