E2Eテストの安定性を高めるTypeScript製パッケージ @itandi/playwright

はじめに

こんにちは!イタンジ株式会社のFrontendチームに所属している西野です。

昨年末に「ITANDI QC」というE2Eテスト基盤を作成し、今年から本格的にE2Eテストを拡充させています。

E2Eテストを何も考慮せずに追加していくと、アプリケーションコードの変更によってすぐに壊れてしまうテストが量産されてしまいます。

そのため、修正する必要のない変更に強いテストを書く方法を検討しましたが、コンポーネントを操作するテストコードの書き方が課題となりました。

本記事ではFrontendチームがE2Eテストの安定性を高めるために取り組んでいる内容を紹介します。

コンポーネント操作の標準化

ITANDI BB UI というコンポーネントライブラリを使って各プロダクトはUIの開発を行っています。

何も考えずにITANDI BB UIのコンポーネントを操作するE2Eテストを書くと、className やDOM構造に依存したテストコードになりがちです。

以下の理由から、我々FrontendチームとしてはITANDI BB UIのコンポーネントの className とDOMに依存したテストコードを生成してほしくありません。

  1. className はCSSフレームワークをSassからEmotionに変更したときの名残であるため(例外として、itandi-bb-ui__Button のようなコンポーネント名を示す className は意図的に残しています)
  2. 機能追加やリファクタリングでDOM構造を変えることがあるため

例えば、MultiComboBoxという複数の値を選択するためのコンポーネントを操作しようとすると下記のようなテストコードになります。

// MultiComboBoxで値を選択
const input = page.locator(".itandi-bb-ui__MultiComboBox__Input");
const labels = ["オプション1", "オプション2"];
for (const label of labels) {
    await input.fill(label);
    await expect(page.locator(".itandi-bb-ui__ComboBoxMenu")).toBeVisible();
    await page
        .locator(".itandi-bb-ui__ComboBoxMenu__Option > div > div")
        .getByText(label, { exact: true })
        .click();
}
await input.blur();

// MultiComboBoxが期待する値となっていることを確認
const values = ["option1", "option2"];
const hiddenInputsLocator = page.locator(
    ".itandi-bb-ui__MultiComboBox input[hidden]"
);
await expect(hiddenInputsLocator).toHaveCount(values.length);
for (const [i, value] of values.entries()) {
    await expect(hiddenInputsLocator.nth(i)).toHaveValue(value);
}

上記のように className とDOM構造に依存していると、ITANDI BB UIをアップデートした後のE2Eテストは、元々指定していた className や要素が存在しなくなると失敗してしまいます。

@itandi/playwright によるコンポーネント操作の抽象化

課題の解決策として、ITANDI BB UIを操作するためのヘルパー関数を集めた社内パッケージ@itandi/playwrightを作成しました。

例えば、前述したMultiComboBoxを操作するコードは下記のようになります。

// MultiComboBoxを操作するためのオブジェクトを取得
const multiComboBox = getMultiComboBox(page);

// 指定したラベルを選択して期待する値になっていることをテスト
await multiComboBox.select(["オプション1", "オプション2"]);
await multiComboBox.toHaveValue(["option1", "option2"]);

関数は get${コンポーネント名} の形式で作成しており、他にも getInputgetDropZone など50を超える関数が存在します。

関数にTSDocを書き、TypeDoc でMDXに変換し、ITANDI BB UIのStorybookにドキュメントとして表示しています。

toBeVisible()toBeHidden() などの頻出のメソッドは各関数に共通で存在しており、getInput には setValue()toHaveValue()getDropZone には upload()deleteFiles() などの固有のメソッドが存在します。

ITANDI BB UIと @itandi/playwright は同じバージョンで管理されており、同じバージョンを使用すれば、アップデートが原因でE2Eテストが失敗することはありません。

ITANDI QCのリポジトリではpnpm workspacesを採用しており、各プロダクトがワークスペースになっています。そのため、各プロダクトはITANDI BB UIのバージョンに合わせた @itandi/playwright を使用できます。

さらにPR作成時に実行されるworkflowで、変更されたファイルが属しているワークスペースからプロダクトのITANDI BB UIのバージョンを特定し、@itandi/playwright とバージョンが一致しているかどうかをチェックしています。
バージョンが一致していない場合はCIが失敗するため、バージョンの不一致にすぐに気づくことができます。

Agent Skillsによるプロダクトチームの実装支援

ITANDIにはQAチームが存在しておらず、FrontendチームがITANDI QCを運用し、E2Eテストの実装は各プロダクトチームが行っています。

各プロダクトチームのエンジニアはPlaywrightでテストコードを書くことに慣れていないため、Playwrightと @itandi/playwright の書き方を覚えなければいけません。

そこで、負担を軽減するためにPlaywrightで実装されたテストコードを @itandi/playwright を使った書き方にするAgent Skillsを作成しました。

Agent Skillsは下記のステップで実行されます。

  1. テストコードのtraceファイル(Trace viewer で使われるzipファイル)を解析する
  2. @itandi/playwright 内のコードから使用できそうな関数を特定する
  3. テストコードを書き換える
  4. 変更内容を表形式で出力する

このSkillを活用することによって、Playwrightや @itandi/playwright の経験が少ないエンジニアでも少ない工数でテストコードを生成できます。

おわりに

AIで大量のコードを高速に生成できるようになったことで、E2Eテストなどによる品質保証の重要性が高まっていると感じています。

ITANDI QCと @itandi/playwright を使って高速にテストコードを量産していける体制が整ったので、直近はプロダクトチームをフォローしつつE2Eテストの拡充やFlakyテストの修正を行っています。

必要十分なテストが揃ってきたら、AIエージェントなどを使って爆速に機能開発やリファクタリングができるようになります。

プロダクトチームが必要なテストシナリオを爆速で実装できるように、FrontendチームでITANDI QCと @itandi/playwright を改善していきます!

採用情報

Frontendチーム、絶賛採用中です!カジュアル面談もお待ちしています!