アサーション
概要
Playwrightには、expect
関数という形式のテストアサーションが含まれています。アサーションを行うには、expect(value)
を呼び出し、期待を反映するマッチャーを選択します。toEqual
、toContain
、toBeTruthy
のような多くの一般的なマッチャーがあり、あらゆる条件をアサートするために使用できます。
expect(success).toBeTruthy();
Playwrightには、期待される条件が満たされるまで待機するウェブ固有の非同期マッチャーも含まれています。以下の例を検討してください
await expect(page.getByTestId('status')).toHaveText('Submitted');
Playwrightは、取得した要素が"Submitted"
テキストを持つまで、status
のテストIDを持つ要素を再テストします。条件が満たされるか、タイムアウトに達するまで、要素を繰り返し再取得してチェックします。このタイムアウトは直接渡すか、テスト設定の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
関数の2番目の引数としてカスタムexpectメッセージを指定できます。例えば
await expect(page.getByText('Name'), 'should be logged in').toBeVisible();
このメッセージはレポーターに表示され、成功したexpectと失敗したexpectの両方で、アサーションに関するより多くのコンテキストを提供します。
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.poll
を使用すると、任意の同期expect
を非同期のポーリング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',
// 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タイムアウトを尊重しないことに注意してください。
expect.extendを使用してカスタムマッチャーを追加する
カスタムマッチャーを提供することで、Playwrightのアサーションを拡張できます。これらのマッチャーはexpect
オブジェクトで利用可能になります。
この例では、カスタムのtoHaveAmount
関数を追加します。カスタムマッチャーは、アサーションが成功したかどうかを示すpass
フラグと、アサーションが失敗したときに使用されるmessage
コールバックを返す必要があります。
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
を使用できます。
import { test, expect } from './fixtures';
test('amount', async () => {
await expect(page.locator('.cart')).toHaveAmount(4);
});
expectライブラリとの互換性
Playwrightのexpect
をexpect
ライブラリと混同しないでください。後者はPlaywrightテストランナーと完全に統合されていないため、必ずPlaywright独自のexpect
を使用してください。
複数のモジュールからカスタムマッチャーを結合する
複数のファイルやモジュールからカスタムマッチャーを結合できます。
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);
import { test, expect } from './fixtures';
test('passes', async ({ database }) => {
await expect(database).toHaveDatabaseUser('admin');
});