スナップショットテスト
概要
Playwright のスナップショットテストを使用すると、ページのアクセシビリティツリーを事前に定義されたスナップショットテンプレートに対してアサートできます。
await page.goto('https://playwright.dokyumento.jp/');
await expect(page.getByRole('banner')).toMatchAriaSnapshot(`
- banner:
- heading /Playwright enables reliable end-to-end/ [level=1]
- link "Get started"
- link "Star microsoft/playwright on GitHub"
- link /[\\d]+k\\+ stargazers on GitHub/
`);
アサーションテスト vs スナップショットテスト
スナップショットテストとアサーションテストは、テスト自動化において異なる目的を果たします。
アサーションテスト
アサーションテストは、要素またはコンポーネントに関する特定の値をアサートするターゲットを絞ったアプローチです。たとえば、Playwright では、expect(locator).toHaveText() は要素に期待されるテキストが含まれていることを検証し、expect(locator).toHaveValue() は入力フィールドに期待される値があることを確認します。アサーションテストは具体的で、一般的に要素またはプロパティの現在の状態を、期待される事前定義された状態に対してチェックします。これらは、予測可能で単一の値のチェックには適していますが、より広範な構造やバリエーションをテストする場合には範囲が限られます。
利点
- 明確さ: テストの意図は明確で理解しやすいです。
- 具体性: テストは機能の特定の部分に焦点を当てているため、無関係な変更に対してより堅牢になります。
- デバッグ: エラーはターゲットを絞ったフィードバックを提供し、問題のある側面を直接指摘します。
欠点
- 複雑な出力には冗長: 複雑なデータ構造または大きな出力のアサーションを記述するのは、面倒でエラーが発生しやすい可能性があります。
- メンテナンスのオーバーヘッド: コードが進化するにつれて、アサーションを手動で更新するのは時間がかかる可能性があります。
スナップショットテスト
スナップショットテストは、特定の瞬間の要素、コンポーネント、またはデータの状態全体の「スナップショット」または表現をキャプチャし、それを将来の比較のために保存します。テストを再実行すると、現在の状態がスナップショットと比較され、相違がある場合はテストが失敗します。このアプローチは、複雑または動的な構造、つまり各詳細を手動でアサートするには時間がかかりすぎる場合に特に役立ちます。スナップショットテストは、アサーションテストよりも広範囲で全体論的であり、時間の経過とともに、より複雑な変更を追跡できます。
利点
- 複雑な出力を簡素化: たとえば、UI コンポーネントのレンダリングされた出力を従来の表明でテストするのは面倒な場合があります。スナップショットは、簡単な比較のために出力全体をキャプチャします。
- 迅速なフィードバックループ: 開発者は、出力の意図しない変更を簡単に見つけることができます。
- 一貫性を促進: コードが進化するにつれて、一貫した出力を維持するのに役立ちます。
欠点
- 過度の依存: スナップショットへの変更を完全に理解せずに受け入れてしまう誘惑に駆られ、バグを隠してしまう可能性があります。
- 粒度: 大きなスナップショットは、特に小さな変更が出力の大部分に影響を与える場合、相違が発生したときに解釈するのが難しい場合があります。
- 適合性: 出力が頻繁または予測不可能に変化する高度に動的なコンテンツには理想的ではありません。
いつ使用するか
- スナップショットテストは以下に最適です。
- ページとコンポーネント全体の UI テスト。
- 複雑な UI コンポーネントの広範な構造チェック。
- 構造がめったに変更されない出力のリグレッションテスト。
- アサーションテストは以下に最適です。
- コアロジックの検証。
- 計算値のテスト。
- 正確な条件を必要とするきめ細かいテスト。
広範な構造チェックにはスナップショットテストを、特定の機能にはアサーションテストを組み合わせることで、バランスの取れたテスト戦略を実現できます。
Aria スナップショット
Playwright では、aria スナップショットはページのアクセシビリティツリーの YAML 表現を提供します。これらのスナップショットを保存して後で比較し、ページ構造が一貫性を保っているか、定義された期待値を満たしているかを確認できます。
YAML 形式は、ページ上のアクセス可能な要素の階層構造を記述し、ロール、属性、値、およびテキストコンテンツを詳細に示します。構造はツリーのような構文に従い、各ノードはアクセス可能な要素を表し、インデントはネストされた要素を示します。
ツリー内の各アクセス可能な要素は YAML ノードとして表されます。
- role "name" [attribute=value]
- ロール: 要素の ARIA または HTML ロールを指定します (例:
heading
、list
、listitem
、button
)。 - "name": 要素のアクセス可能な名前。引用符で囲まれた文字列は正確な値を示し、
/patterns/
は正規表現に使用されます。 - [attribute=value]: 属性と値 (角括弧内) は、
checked
、disabled
、expanded
、level
、pressed
、またはselected
などの特定の ARIA 属性を表します。
これらの値は、ARIA 属性から派生するか、HTML セマンティクスに基づいて計算されます。ページのアクセシビリティツリー構造を検査するには、Chrome DevTools Accessibility Pane を使用します。
スナップショットマッチング
Playwright の expect(locator).toMatchAriaSnapshot() アサーションメソッドは、ロケータースコープのアクセス可能な構造を事前定義された aria スナップショットテンプレートと比較し、ページのステートをテスト要件に対して検証するのに役立ちます。
次の DOM の場合
<h1>title</h1>
次のスナップショットテンプレートを使用して一致させることができます。
await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading "title"
`);
マッチング時、スナップショットテンプレートはページの現在のアクセシビリティツリーと比較されます。
- ツリー構造がテンプレートと一致する場合、テストは成功します。それ以外の場合は失敗し、期待されるアクセシビリティ状態と実際のアクシビリティ状態の不一致を示します。
- 比較は大文字と小文字を区別し、空白を折りたたむため、インデントと改行は無視されます。
- 比較は順序に依存します。つまり、スナップショットテンプレート内の要素の順序は、ページのアクセシビリティツリー内の順序と一致する必要があります。
部分一致
属性またはアクセス可能な名前を省略することで、ノードに対して部分一致を実行できます。これにより、正確な一致を必要とせずに、アクセシビリティツリーの特定の部分の検証が可能になります。この柔軟性は、動的属性または無関係な属性に役立ちます。
<button>Submit</button>
aria スナップショット
- button
この例では、button ロールは一致していますが、アクセス可能な名前 ("Submit") は指定されていないため、テストはボタンのラベルに関係なく合格します。
checked
や disabled
などの ARIA 属性を持つ要素の場合、これらの属性を省略すると部分一致が可能になり、ロールと階層のみに焦点を当てることができます。
<input type="checkbox" checked>
部分一致の aria スナップショット
- checkbox
この部分一致では、checked
属性は無視されるため、テストはチェックボックスの状態に関係なく合格します。
同様に、特定リストアイテムまたはネストされた要素を省略することで、リストまたはグループ内の子を部分的に一致させることができます。
<ul>
<li>Feature A</li>
<li>Feature B</li>
<li>Feature C</li>
</ul>
部分一致の aria スナップショット
- list
- listitem: Feature B
部分一致を使用すると、特定のコンテンツや属性を強制することなく、重要なページ構造を検証する柔軟なスナップショットテストを作成できます。
正規表現を使用したマッチング
正規表現を使用すると、動的または可変テキストを持つ要素に対して柔軟なマッチングが可能です。アクセス可能な名前とテキストは、正規表現パターンをサポートできます。
<h1>Issues 12</h1>
正規表現を使用した aria スナップショット
- heading /Issues \d+/
スナップショットの生成
Playwright で aria スナップショットを作成すると、アプリケーションの構造を確保および維持するのに役立ちます。テストのセットアップとワークフローに応じて、さまざまな方法でスナップショットを生成できます。
Playwright コードジェネレーターを使用したスナップショットの生成
Playwright の コードジェネレーター を使用している場合、aria スナップショットの生成はインタラクティブインターフェイスで合理化されています。
- "スナップショットをアサート" アクション: コードジェネレーターでは、"スナップショットをアサート" アクションを使用して、選択した要素のスナップショットアサーションを自動的に作成できます。これは、記録されたテストフローの一部として aria スナップショットをキャプチャする簡単な方法です。
- "Aria スナップショット" タブ: コードジェネレーターインターフェイス内の "Aria スナップショット" タブは、選択したロケーターの aria スナップショットを視覚的に表現し、要素のロール、属性、およびアクセス可能な名前を調べて検査し、スナップショットの作成とレビューを支援します。
@playwright/test
と --update-snapshots
フラグを使用したスナップショットの更新
Playwright テストランナー (@playwright/test
) を使用する場合、--update-snapshots
フラグ (短縮形の -u
) を使用してスナップショットを自動的に更新できます。
--update-snapshots
フラグを指定してテストを実行すると、一致しなかったスナップショットが更新されます。一致したスナップショットは更新されません。
npx playwright test --update-snapshots
スナップショットの更新は、アプリケーション構造の変更に新しいスナップショットをベースラインとして必要とする場合に役立ちます。Playwright は、スナップショットを取得する前にページが落ち着くまで、テストランナー構成で指定された最大期待タイムアウトまで待機することに注意してください。スナップショットの生成中にテストがタイムアウトになった場合は、--timeout
を調整する必要がある場合があります。
スナップショット生成用の空のテンプレート
アサーションで空の文字列をテンプレートとして渡すと、オンザフライでスナップショットが生成されます。
await expect(locator).toMatchAriaSnapshot('');
Playwright は、スナップショットを取得する前にページが落ち着くまで、テストランナー構成で指定された最大期待タイムアウトまで待機することに注意してください。スナップショットの生成中にテストがタイムアウトになった場合は、--timeout
を調整する必要がある場合があります。
スナップショットパッチファイル
スナップショットを更新すると、Playwright は差異をキャプチャするパッチファイルを作成します。これらのパッチファイルは、レビュー、適用、およびソース管理にコミットでき、チームは時間の経過に伴う構造的変更を追跡し、更新がアプリケーション要件と一致していることを確認できます。
ソースコードの更新方法は、--update-source-method
フラグを使用して変更できます。いくつかのオプションがあります。
- "patch" (デフォルト):
git apply
を使用してソースコードに適用できる unified diff ファイルを生成します。 - "3way": ソースコードにマージコンフリクトマーカーを生成し、変更を受け入れるかどうかを選択できるようにします。
- "overwrite": ソースコードを新しいスナップショット値で上書きします。
npx playwright test --update-snapshots --update-source-mode=3way
別ファイルとしてのスナップショット
スナップショットを別のファイルに保存するには、toMatchAriaSnapshot
メソッドを name
オプションで使用し、.aria.yml
ファイル拡張子を指定します。
await expect(page.getByRole('main')).toMatchAriaSnapshot({ name: 'main.aria.yml' });
デフォルトでは、テストファイル example.spec.ts
からのスナップショットは、example.spec.ts-snapshots
ディレクトリに配置されます。スナップショットはブラウザ間で同じである必要があるため、複数のブラウザでテストする場合でも、保存されるスナップショットは 1 つだけです。必要に応じて、次の構成を使用してスナップショットパスのテンプレートをカスタマイズできます。
export default defineConfig({
expect: {
toMatchAriaSnapshot: {
pathTemplate: '__snapshots__/{testFilePath}/{arg}{ext}',
},
},
});
Locator.ariaSnapshot
メソッドの使用
locator.ariaSnapshot() メソッドを使用すると、特にテスト実行中にスナップショットを動的に生成する場合に役立つ、ロケータースコープ内のアクセス可能な要素の YAML 表現をプログラムで作成できます。
例:
const snapshot = await page.locator('body').ariaSnapshot();
console.log(snapshot);
このコマンドは、指定されたロケータースコープ内の aria スナップショットを YAML 形式で出力します。これは、必要に応じて検証または保存できます。
アクセシビリティツリーの例
レベル属性を持つ見出し
見出しには、見出しレベルを示す level
属性を含めることができます。
<h1>Title</h1>
<h2>Subtitle</h2>
aria スナップショット
- heading "Title" [level=1]
- heading "Subtitle" [level=2]
テキストノード
スタンドアロンまたは説明的なテキスト要素は、テキストノードとして表示されます。
<div>Sample accessible name</div>
aria スナップショット
- text: Sample accessible name
インライン複数行テキスト
段落などの複数行テキストは、aria スナップショットで正規化されます。
<p>Line 1<br>Line 2</p>
aria スナップショット
- paragraph: Line 1 Line 2
リンク
リンクは、テキストまたは疑似要素から構成されたコンテンツを表示します。
<a href="#more-info">Read more about Accessibility</a>
aria スナップショット
- link "Read more about Accessibility"
テキストボックス
タイプ text
の入力要素は、value
属性の内容を表示します。
<input type="text" value="Enter your name">
aria スナップショット
- textbox: Enter your name
アイテム付きリスト
順序付きリストと順序なしリストには、リストアイテムが含まれています。
<ul aria-label="Main Features">
<li>Feature 1</li>
<li>Feature 2</li>
</ul>
aria スナップショット
- list "Main Features":
- listitem: Feature 1
- listitem: Feature 2
グループ化された要素
グループは、サマリーコンテンツを含む <details>
要素などのネストされた要素をキャプチャします。
<details>
<summary>Summary</summary>
<p>Detail content here</p>
</details>
aria スナップショット
- group: Summary
属性と状態
checked
、disabled
、expanded
、level
、pressed
、および selected
などの一般的に使用される ARIA 属性は、コントロールの状態を表します。
checked
属性を持つチェックボックス
<input type="checkbox" checked>
aria スナップショット
- checkbox [checked]
pressed
属性を持つボタン
<button aria-pressed="true">Toggle</button>
aria スナップショット
- button "Toggle" [pressed=true]