スナップショットテスト
概要
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/
`);
アサーションテストとスナップショットテストの比較
スナップショットテストとアサーションテストは、テスト自動化において異なる目的で利用されます。
アサーションテスト
アサーションテストは、要素やコンポーネントに関する特定の値や条件をアサートする、的を絞ったアプローチです。例えば、Playwrightでは、expect(locator).toHaveText()
は要素が期待されるテキストを含んでいることを検証し、expect(locator).toHaveValue()
は入力フィールドが期待される値を持っていることを確認します。アサーションテストは具体的で、一般的に要素やプロパティの現在の状態を、期待される事前定義された状態と照合します。予測可能な単一値のチェックには効果的ですが、より広範な構造やバリエーションをテストする際には範囲が限られます。
利点
- 明確性: テストの意図が明確で理解しやすい。
- 特異性: テストは機能の特定の側面に焦点を当てるため、無関係な変更に対してより堅牢です。
- デバッグ: 失敗時には的を絞ったフィードバックが提供され、問題のある側面を直接指摘します。
欠点
- 複雑な出力に対する冗長性: 複雑なデータ構造や大規模な出力に対してアサーションを作成するのは、煩雑でエラーが発生しやすい場合があります。
- メンテナンスのオーバーヘッド: コードが進化するにつれて、アサーションを手動で更新するのは時間がかかる場合があります。
スナップショットテスト
スナップショットテストは、要素、コンポーネント、またはデータの特定の時点での全体の状態の「スナップショット」または表現をキャプチャし、将来の比較のために保存します。テストを再実行する際、現在の状態がスナップショットと比較され、差異があればテストは失敗します。このアプローチは、複雑なまたは動的な構造において特に役立ちます。手動で各詳細をアサートするのは時間がかかりすぎるためです。スナップショットテストはアサーションテストよりも広範で包括的であり、時間の経過とともに発生するより複雑な変更を追跡できます。
利点
- 複雑な出力を簡素化: 例えば、UIコンポーネントのレンダリング出力を従来のAssertionでテストするのは面倒です。スナップショットは全体の出力をキャプチャし、簡単に比較できます。
- 迅速なフィードバックループ: 開発者は出力における意図しない変更を簡単に見つけることができます。
- 一貫性を促進: コードの進化に伴い、一貫した出力を維持するのに役立ちます。
欠点
- 過度な依存: スナップショットの変更を完全に理解せずに受け入れてしまいがちで、潜在的にバグを隠してしまう可能性があります。
- 粒度: 差異が発生した場合、特に軽微な変更が出力の大半に影響する場合、大きなスナップショットは解釈が難しいことがあります。
- 適性: 出力が頻繁にまたは予測不能に変化するような非常に動的なコンテンツには適していません。
使用するタイミング
- スナップショットテストは次の場合に理想的です。
- ページ全体やコンポーネントのUIテスト。
- 複雑なUIコンポーネントの広範な構造チェック。
- 構造がめったに変わらない出力に対する回帰テスト。
- アサーションテストは次の場合に理想的です。
- コアロジックの検証。
- 計算値のテスト。
- 正確な条件を必要とするきめ細かいテスト。
広範な構造チェックにはスナップショットテストを、特定の機能にはアサーションテストを組み合わせることで、バランスの取れたテスト戦略を実現できます。
Ariaスナップショット
Playwrightでは、ARIAスナップショットはページのアクセシビリティツリーをYAML形式で表現します。これらのスナップショットは保存され、後で比較することで、ページの構造が一貫しているか、または定義された期待を満たしているかを確認できます。
YAML形式は、ページ上のアクセシブルな要素の階層構造を記述し、ロール、属性、値、およびテキストコンテンツを詳述します。この構造はツリー状の構文に従い、各ノードがアクセシブルな要素を表し、インデントがネストされた要素を示します。
ツリー内の各アクセシブルな要素はYAMLノードとして表現されます
- role "name" [attribute=value]
- role: 要素のARIAまたはHTMLロールを指定します (例:
heading
,list
,listitem
,button
)。 - "name": 要素のアクセシブル名。引用符で囲まれた文字列は正確な値を表し、
/patterns/
は正規表現に使用されます。 - [attribute=value]: 角括弧で囲まれた属性と値は、
checked
、disabled
、expanded
、level
、pressed
、selected
などの特定のARIA属性を表します。
これらの値はARIA属性から派生するか、HTMLのセマンティクスに基づいて計算されます。ページのアクセシビリティツリー構造を検査するには、Chrome DevToolsのアクセシビリティタブを使用してください。
スナップショットのマッチング
Playwrightのexpect(locator).toMatchAriaSnapshot()
アサーションメソッドは、ロケータースコープのアクセシブルな構造を事前定義されたARIAスナップショットテンプレートと比較し、ページのDOM状態をテスト要件に対して検証するのに役立ちます。
以下のDOMの場合
<h1>title</h1>
以下のスナップショットテンプレートを使用してマッチングできます
await expect(page.locator('body')).toMatchAriaSnapshot(`
- heading "title"
`);
マッチング時、スナップショットテンプレートはページの現在のアクセシビリティツリーと比較されます。
- ツリー構造がテンプレートと一致する場合、テストは合格します。そうでない場合、期待されるアクセシビリティ状態と実際のアクセシビリティ状態の不一致を示してテストは失敗します。
- 比較は大文字と小文字を区別し、空白を詰めるため、インデントや改行は無視されます。
- 比較は順序に依存するため、スナップショットテンプレート内の要素の順序は、ページのアクセシビリティツリー内の順序と一致する必要があります。
部分一致
属性やアクセシブル名を省略することでノードの部分一致を実行でき、厳密な一致を必要とせずにアクセシビリティツリーの特定の部分を検証できます。この柔軟性は、動的または関連性の低い属性に役立ちます。
<button>Submit</button>
ARIAスナップショット
- 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
部分一致を使用すると、特定のコンテンツや属性を強制することなく、重要なページ構造を検証する柔軟なスナップショットテストを作成できます。
厳密なマッチング
デフォルトでは、子要素のサブセットを含むテンプレートがマッチングされます。
<ul>
<li>Feature A</li>
<li>Feature B</li>
<li>Feature C</li>
</ul>
部分一致のためのARIAスナップショット
- list
- listitem: Feature B
/children
プロパティを使用して、子要素がどのようにマッチングされるかを制御できます。
contain
(デフォルト): 指定されたすべての子要素が順序通りに存在する場合にマッチングします。equal
: 子要素が指定されたリストと順序を含め正確に一致する場合にマッチングします。deep-equal
: 子要素が指定されたリストと順序を含め正確に一致する場合にマッチングします(ネストされた子要素も含む)。
<ul>
<li>Feature A</li>
<li>Feature B</li>
<li>Feature C</li>
</ul>
Feature Cがテンプレートにないため、ARIAスナップショットは失敗します。
- list
- /children: equal
- listitem: Feature A
- listitem: Feature B
正規表現によるマッチング
正規表現を使用すると、動的または可変のテキストを持つ要素に対して柔軟なマッチングが可能です。アクセシブル名とテキストは正規表現パターンをサポートできます。
<h1>Issues 12</h1>
正規表現を含むARIAスナップショット
- heading /Issues \d+/
スナップショットの生成
PlaywrightでARIAスナップショットを作成することは、アプリケーションの構造を確保し、維持するのに役立ちます。テストのセットアップとワークフローに応じて、さまざまな方法でスナップショットを生成できます。
Playwrightコードジェネレーターでのスナップショット生成
Playwrightのコードジェネレーターを使用している場合、インタラクティブなインターフェースでARIAスナップショットの生成が効率化されます。
- 「Assert snapshot」アクション: コードジェネレーターでは、「Assert snapshot」アクションを使用して、選択した要素のスナップショットアサーションを自動的に作成できます。これは、記録されたテストフローの一部としてARIAスナップショットをキャプチャする迅速な方法です。
- 「Aria snapshot」タブ: コードジェネレーターインターフェース内の「Aria snapshot」タブは、選択されたロケーターの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
を使用してソースコードに適用できる統一差分ファイルを生成します。 - 「3way」: ソースコードにマージ競合マーカーを生成し、変更を受け入れるかどうかを選択できるようにします。
- 「overwrite」: ソースコードを新しいスナップショット値で上書きします。
npx playwright test --update-snapshots --update-source-method=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]