Skip to main content

斷言

簡介

Playwright 以 expect 函式的形式包含測試斷言。要進行斷言,請呼叫 expect(value) 並選擇反映預期的比對器。有許多通用比對器toEqualtoContaintoBeTruthy 可用來斷言任何條件。

expect(success).toBeTruthy();

Playwright 也包含網頁特定的非同步比對器,會等待直到預期條件滿足。考慮以下範例:

await expect(page.getByTestId('status')).toHaveText('Submitted');

Playwright 會重新測試具有 status 測試 ID 的元素,直到獲取的元素具有 "Submitted" 文字。它會重新獲取元素並一遍又一遍地檢查,直到滿足條件或達到逾時。您可以傳遞此逾時,或透過測試組態中的 testConfig.expect 值一次性設定。

預設情況下,斷言的逾時設定為 5 秒。了解更多各種逾時

自動重試斷言

以下斷言會重試直到斷言通過,或達到斷言逾時。請注意重試斷言是非同步的,所以您必須 await 它們。

斷言描述
await expect(locator).toBeAttached()元素已附加
await expect(locator).toBeChecked()核取方塊已選取
await expect(locator).toBeDisabled()元素已停用
await expect(locator).toBeEditable()元素可編輯
await expect(locator).toBeEmpty()容器為空
await expect(locator).toBeEnabled()元素已啟用
await expect(locator).toBeFocused()元素已聚焦
await expect(locator).toBeHidden()元素不可見
await expect(locator).toBeInViewport()元素與檢視區相交
await expect(locator).toBeVisible()元素可見
await expect(locator).toContainText()元素包含文字
await expect(locator).toContainClass()元素具有指定的 CSS 類別
await expect(locator).toHaveAccessibleDescription()元素具有相符的無障礙描述
await expect(locator).toHaveAccessibleName()元素具有相符的無障礙名稱
await expect(locator).toHaveAttribute()元素具有 DOM 屬性
await expect(locator).toHaveClass()元素具有指定的 CSS 類別屬性
await expect(locator).toHaveCount()清單具有確切的子項目數量
await expect(locator).toHaveCSS()元素具有 CSS 屬性
await expect(locator).toHaveId()元素具有 ID
await expect(locator).toHaveJSProperty()元素具有 JavaScript 屬性
await expect(locator).toHaveRole()元素具有特定的 ARIA 角色
await expect(locator).toHaveScreenshot()元素具有螢幕截圖
await expect(locator).toHaveText()元素符合文字
await expect(locator).toHaveValue()輸入具有值
await expect(locator).toHaveValues()選擇具有選取的選項
await expect(locator).toMatchAriaSnapshot()元素符合 Aria 快照
await expect(page).toHaveScreenshot()頁面具有螢幕截圖
await expect(page).toHaveTitle()頁面具有標題
await expect(page).toHaveURL()頁面具有 URL
await expect(response).toBeOK()回應具有 OK 狀態

非重試斷言

這些斷言允許測試任何條件,但不會自動重試。大多數時候,網頁會非同步顯示資訊,使用非重試斷言可能導致不穩定的測試。

盡可能優先使用自動重試斷言。對於需要重試的更複雜斷言,使用 expect.pollexpect.toPass

斷言描述
expect(value).toBe()值相同
expect(value).toBeCloseTo()數字大約相等
expect(value).toBeDefined()值不是 undefined
expect(value).toBeFalsy()值為假值,例如 false0null
expect(value).toBeGreaterThan()數字大於
expect(value).toBeGreaterThanOrEqual()數字大於或等於
expect(value).toBeInstanceOf()物件是類別的執行個體
expect(value).toBeLessThan()數字小於
expect(value).toBeLessThanOrEqual()數字小於或等於
expect(value).toBeNaN()值是 NaN
expect(value).toBeNull()值是 null
expect(value).toBeTruthy()值為真值,即不是 false0null
expect(value).toBeUndefined()值是 undefined
expect(value).toContain()字串包含子字串
expect(value).toContain()陣列或集合包含元素
expect(value).toContainEqual()陣列或集合包含相似元素
expect(value).toEqual()值相似 - 深度相等和模式比對
expect(value).toHaveLength()陣列或字串具有長度
expect(value).toHaveProperty()物件具有屬性
expect(value).toMatch()字串符合正規表示式
expect(value).toMatchObject()物件包含指定屬性
expect(value).toStrictEqual()值相似,包括屬性類型
expect(value).toThrow()函式拋出錯誤
expect(value).any()符合類別/基本型別的任何執行個體
expect(value).anything()符合任何東西
expect(value).arrayContaining()陣列包含特定元素
expect(value).closeTo()數字大約相等
expect(value).objectContaining()物件包含特定屬性
expect(value).stringContaining()字串包含子字串
expect(value).stringMatching()字串符合正規表示式

否定比對器

一般來說,我們可以在比對器前面加上 .not 來預期相反的結果為真:

expect(value).not.toEqual(0);
await expect(locator).not.toContainText('some text');

軟斷言

預設情況下,失敗的斷言會終止測試執行。Playwright 也支援軟斷言:失敗的軟斷言不會終止測試執行,但會將測試標記為失敗。

// 進行一些檢查,失敗時不會停止測試...
await expect.soft(page.getByTestId('status')).toHaveText('Success');
await expect.soft(page.getByTestId('eta')).toHaveText('1 day');

// ...並繼續測試以檢查更多內容。
await page.getByRole('link', { name: 'next page' }).click();
await expect.soft(page.getByRole('heading', { name: 'Make another order' })).toBeVisible();

在測試執行期間的任何時候,您可以檢查是否有任何軟斷言失敗:

// 進行一些檢查,失敗時不會停止測試...
await expect.soft(page.getByTestId('status')).toHaveText('Success');
await expect.soft(page.getByTestId('eta')).toHaveText('1 day');

// 如果有軟斷言失敗,避免繼續執行。
expect(test.info().errors).toHaveLength(0);

請注意,軟斷言只在 Playwright 測試執行器中有效。

自訂預期訊息

您可以指定自訂預期訊息作為 expect 函式的第二個參數,例如:

await expect(page.getByText('Name'), 'should be logged in').toBeVisible();

此訊息會在報告器中顯示,無論預期通過或失敗,都會提供關於斷言的更多背景資訊。

當預期通過時,您可能會看到這樣的成功步驟:

✅ should be logged in    @example.spec.ts:18

當預期失敗時,錯誤會看起來像這樣:

    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 執行個體,擁有自己的預設值,如 timeoutsoft

const slowExpect = expect.configure({ timeout: 10000 });
await slowExpected(locator).toHaveText('Submit');

// 始終使用軟斷言。
const softExpect = expect.configure({ soft: true });
await softExpect(locator).toHaveText('Submit');

expect.poll

您可以使用 expect.poll 將任何同步 expect 轉換為非同步輪詢。

以下方法會輪詢指定函式,直到它回傳 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',
// 輪詢 10 秒;預設為 5 秒。傳遞 0 以停用逾時。
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.configure({ soft: true }) 與 expect.poll 結合,在輪詢邏輯中執行軟斷言。

const softExpect = expect.configure({ soft: true });
await softExpect.poll(async () => {
const response = await page.request.get('https://api.example.com');
return response.status();
}, {}).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 逾時

Add custom matchers using expect.extend

您可以透過提供自訂比對器來擴充 Playwright 斷言。這些比對器將在 expect 物件上可用。

在此範例中,我們新增自訂的 toHaveAmount 函式。自訂比對器應傳回 pass 旗標,指示斷言是否通過,以及在斷言失敗時使用的 message 回呼。

fixtures.ts
import { expect as baseExpect } from '@playwright/test';
import type { 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 {
const expectation = this.isNot ? baseExpect(locator).not : baseExpect(locator);
await expectation.toHaveAttribute('data-amount', String(expected), options);
pass = true;
} catch (e: any) {
matcherResult = e.matcherResult;
pass = false;
}

if (this.isNot) {
pass =!pass;
}

const message = pass
? () => this.utils.matcherHint(assertionName, undefined, undefined, { isNot: this.isNot }) +
'\n\n' +
`Locator: ${locator}\n` +
`Expected: 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

example.spec.ts
import { test, expect } from './fixtures';

test('amount', async () => {
await expect(page.locator('.cart')).toHaveAmount(4);
});

與 expect 函式庫的相容性

note

請勿將 Playwright 的 expectexpect 函式庫混淆。後者沒有與 Playwright 測試執行器完全整合,因此請確保使用 Playwright 自己的 expect

結合來自多個模組的自訂比對器

您可以結合來自多個檔案或模組的自訂比對器。

fixtures.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);
test.spec.ts
import { test, expect } from './fixtures';

test('passes', async ({ database }) => {
await expect(database).toHaveDatabaseUser('admin');
});