斷言
簡介
Playwright 包含以 expect
函式形式的測試斷言。要進行斷言,呼叫 expect(value)
並選擇反映期望的匹配器。有許多像 toEqual
、toContain
、toBeTruthy
這樣的通用匹配器可以用來斷言任何條件。
expect(success).toBeTruthy();
Playwright 也包括特定於網頁的 非同步匹配器,它會等待直到預期條件滿足。請考慮以下範例:
await expect(page.getByTestId('status')).toHaveText('Submitted');
Playwright 將會重新測試具有 status
測試 id 的元素,直到獲取的元素具有 "Submitted"
文本。它會重新獲取元素並一遍又一遍地檢查,直到條件滿足或超時為止。你可以傳遞這個超 時時間,或者通過測試配置中的 testConfig.expect 值來配置一次。
預設情況下,斷言的超時時間設置為 5 秒。了解更多關於各種超時。
自動重試斷言
以下斷言將重試直到斷言通過,或達到斷言超時。請注意,重試斷言是非同步的,所以你必須 await
它們。
非重試斷言
這些斷言允許測試任意條件,但不會自動重試。大多數時候,網頁會異步顯示資訊,使用不重試的斷言可能會導致測試不穩定。
盡可能偏好自動重試斷言。對於需要重試的更複雜斷言,使用expect.poll
或expect.toPass
。
否定匹配器
一般來說,我們可以通過在匹配器前面添加 .not
來預期相反的結果:
expect(value).not.toEqual(0);
await expect(locator).not.toContainText('some text');
軟性斷言
預設情況下,斷言失敗將終止測試執行。Playwright 也支持軟斷言:軟斷言失敗不會終止測試執行,但會將測試標記為失敗。
// 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();
在測試執行的任何時候,你可以檢查是否有任何軟性斷言失敗:
// 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);
注意,軟斷言僅適用於 Playwright 測試執行器。
自訂 expect 訊息
您可以指定一個自訂的預期訊息作為 expect
函式的第二個參數,例如:
await expect(page.getByText('Name'), 'should be logged in').toBeVisible();
此訊息將顯示在報告中,無論是通過還是失敗的期望,提供有關斷言的更多上下文。
當 expect 通過時,你可能會看到像這樣的成功步驟:
✅ should be logged in @example.spec.ts:18
當 expect 失敗時,錯誤會看起來像這樣:
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 |
軟性斷言也支持自訂訊息:
expect.soft(value, 'my soft assertion').toBe(56);
expect.configure
你可以建立你自己的預先配置的 expect
實例,以擁有其自己的預設值,例如 timeout
和 soft
。
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
你可以將任何同步的 expect
轉換為非同步的輪詢,使用 expect.poll
。
以下方法將輪詢給定函式直到它返回 HTTP 狀態 200:
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);
您也可以指定自訂的輪詢間隔:
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
你可以重試程式碼區塊直到它們成功通過。
await expect(async () => {
const response = await page.request.get('https://api.example.com');
expect(response.status()).toBe(200);
}).toPass();
您也可以指定自訂的逾時和重試間隔:
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
});
請注意,預設情況下 toPass
的超時時間為 0,且不會遵循自訂的 expect timeout。
使用 expect.extend 添加自定義匹配器
你可以透過提供自訂匹配器來擴展 Playwright 斷言。這些匹配器將可用於 expect
物件。
在這個範例中,我們添加了一個自定義的 toHaveAmount
函式。自定義匹配器應該返回一個 message
回調和一個 pass
旗標,指示斷言是否通過。
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,
};
},
});
現在我們可以在 測試中使用 toHaveAmount
。
import { test, expect } from './fixtures';
test('amount', async () => {
await expect(page.locator('.cart')).toHaveAmount(4);
});
與 expect 函式庫的相容性
不要將 Playwright 的 expect
與 expect
函式庫 混淆。後者並未完全整合到 Playwright 測試執行器中,因此請確保使用 Playwright 自己的 expect
。