📕

[Playwright]E2Eテスト自動化におけるAIコーディングルールの作り方

に公開

こんにちは!アルダグラムでQAエンジニアをしている千葉です!

ここ数年で、AIを使ったコーディングが一般的になり、プロダクトの開発スピードが飛躍的に向上しました。

これにより、UIの変更といった仕様変更が頻繁に起こるようになりE2Eテストコードの整備も今まで以上にスピード感が求められる時代になったのでは?と思います。

今回は、E2Eテストの自動化もAIを前提としたコーディングの環境を整備し、自動化のスピードを向上させよう!ということで、弊社で定義しているE2Eテスト自動化におけるAIコーディングルールをご紹介したいと思います!

前提

  • CursorやClaude code、GitHub CopilotなどAIコーディングアシスタントツールを使用していること
  • E2Eテスト自動化の整備を対象(UTやITは対象外)
  • Playwright MCPとの連携(必須ではありません)

前回私が書いた記事で、前提として記載している環境の構築方法なども記載していますので、併せてご参照ください。

📝Playwright-mcp を使ったE2Eテストスクリプトの作成を試してみた

コーディングルール整備のメリット

簡単にE2Eテストにおけるコーディングルールを整備することのメリットについてAI視点とエンジニア視点からご紹介します。
コーディングルールを整備することで主にこのようなメリットが得られると思っています。

AI視点

  • AIのコード補完精度の向上
    • コーディングルールに沿ったロケーターの補完
    • 既存コードとの一貫性の担保
  • AIのレビュー精度向上
    • コーディングルールに基づいたAIによる改善の提案
    • リポジトリの特性に合わせたコードレビュー

エンジニア視点

  • 可読性向上
    • AIによるコーディング支援によって作成された一貫性のある読みやすいコード
    • コーディングルールにより義務付けられているテスト手順や期待結果のコメント
  • 実装・レビュー指針の策定
    • コーディングルールに基づいた人間による改善の提案
    • レビュイー・レビュアー間でのコーディングルールの共通化

コーディングルールの整備方法

弊社のQAチームでは、CursorGitHub Copilotに対するコーディングルールを整備していますので、それらの整備方法をご紹介します。

Cursor Rules

まずはCursorのコーディングルールの作成方法です。

以下の手順で実施します。

  1. Cursorの設定を開く
  2. Rules & Memoriesを選択
  3. Project RulesのAdd Ruleをクリック
  4. 任意のファイル名を入力
    1. ここでは、coding-rule とします

これでCursor用のコーディングルールの元となるファイルが作成できました。

Copilot instructions

続いて、GitHub Copilotのコーディングルールの作成方法です。

以下の手順で実施します。

  1. リポジトリ直下に.github を作成(すでにある場合は作成不要です)
  2. .github 配下にcopilot-instructions.md を作成

GitHub Copilot用のコーディングルールの元となるファイルがこれで完成です。

これらのファイルに次でご紹介するコーディングルールを設定していきます。

コーディングルールの紹介

コーディングルールに関しては諸説あると思いますので、これが正しい!というものではありませんが、弊社で実際に運用しているE2Eテストスクリプトのコーディングルールをご紹介します。

.cursor/rules/coding-rule.mdc (長いのでトグルに閉じています)
---
description:
globs:
alwaysApply: true
---

### 1. ファイル構成

- テストファイルは tests/ ディレクトリ配下に配置する
- テストファイルの命名規則は \*.spec.ts とする
- URLパスの階層構造に準じてディレクトリを分割する(例:/signin → tests/signin/, /projects/board → tests/projects/board/ など)
- 共通のユーティリティ関数は tests/utils/ ディレクトリに配置する
- ページオブジェクトは pages/ ディレクトリに配置し、URLパスの階層構造に準じる(例:pages/signin/, pages/projects/board/ など)

### 2. テストファイルの基本構造

- テストケースの初めに以下の形式でコメントを記載する:
  ```typescript
  /**
   * [テスト名]
   *
   * [テストの概要説明]
   *
   * テストの目的:
   * - [目的1]
   * - [目的2]
   * ...
   *
   * テスト手順:
   * 1. [手順1]
   * 2. [手順2]
   *    - [サブ手順2-1]
   *    - [サブ手順2-2]
   * ...
   *
   * 期待される結果:
   * - [期待結果1]
   * - [期待結果2]
   * ...
   *
   * 注意事項:
   * - [注意事項1]
   * - [注意事項2]
   * ...
   */
  ```
- 操作するアカウントが同一のテストケースはtest.describe() でまとめる
- テストケースによっては、不要となる場合もあるため、注意事項の記載は必須とせず、任意の入力項目とする。
- 注意事項は、テストの実行に影響を与える可能性のある特記事項や、特定の条件下での挙動を明記することを目的とする。
  - 例:- テスト終了後、作成したテストデータは事後処理で削除されます。

### 3. テストケースの命名規則

- テストケース名は日本語で記述し、具体的な動作と期待される結果を含める
- test()の命名パターン:「テストID 機能名*条件*確認内容」(例:`p-securityIncident-003 案件一覧_自社一般の担当外での確認`- test.step()の命名パターン:「手順番号. 【操作ユーザー】具体的な操作内容」(例:`2. 【自社/オーナー】サイドメニューから「案件一覧」をクリックし、案件一覧画面に遷移する`### 4. ページオブジェクトパターンの使用

- 各画面の要素やアクションは、ページオブジェクトクラスとして実装する
- ページオブジェクトは pages/ ディレクトリに配置する
- 共通のアクションはページオブジェクトのメソッドとして実装する
- ページオブジェクトでは、expectはしない

### 5. PlaywrightのAPI使用

- 基本的にはPlaywrightのAPIを使用し、無闇に自作メソッドを作成しない
- PlaywrightのAPIにないような処理を実装したい場合に他のJavaScriptなどのAPIを使用する

### 6. セレクターの優先順位

- アクセシビリティ属性(role, name)を優先的に使用する
- placeholder 属性を次に使用する
- data-testid 属性を次に使用する
- テキストコンテンツを次に使用する
- class 属性は最終手段として使用する

### 7. アサーション

- 明示的なアサーションを使用する
- 要素の表示確認には toBeVisible() を使用する
- テキストの確認には toHaveText() または toContainText() を使用する
- 入力値の確認には toHaveValue() を使用する
- URLの確認には expect(page.url()).toHaveURL() を使用する

### 8. 待機処理

- 明示的な待機処理を使用する
- ページ遷移の待機には waitForURL() を使用する
- 要素の表示待ちには waitFor() を使用し、必要に応じてタイムアウト値を設定する
- waitForTimeout() は以下の場合に限って使用する:
  - ポーリング処理での短時間待機(100ms程度)
  - 要素の状態変化を待つための定期的なチェック間隔
  - その他の明示的な待機が必要な場合
- 基本的には要素の状態変化を待つ場合は waitFor() を優先する

### 9. テストデータ

- spec間で共通のテストデータは data/ ディレクトリに配置する
- 特定のテストでしか使用しないテストデータの情報などは test() 上部に記載する(過度にまとめることは避ける)
- 環境依存の設定は config.ts で管理する
- テストアカウント情報は accounts.ts で管理する

### 10. 共通処理

- ログイン処理など共通の処理は、ヘルパー関数として実装する
- ヘルパー関数は tests/utils/ ディレクトリに配置する

### 11. エラーハンドリング

- 適切なタイムアウト値を設定する
- エラーメッセージの確認は具体的なテキストで行う
- 非同期処理は適切に await する

### 12. テストの分離

- テストケース間の依存関係をなるべく作らない
- 各テストケースは独立して実行できるようにする
- テストデータのクリーンアップは afterEach または afterAll で行う

### 13. パフォーマンス考慮事項

- CIでの実行時間を考慮してテストを設計する
- 並列実行数は環境に応じて適切に設定する

### 14. デバッグ支援

- スクリーンショットは失敗時のみ取得する
- トレース情報は開発環境でのみ取得する
- HTMLレポートを生成する

### 15. 環境設定

- 環境固有の設定は playwright.config.ts で管理する
- リトライ回数はCI環境でのみ設定する

### 16. 命名規則

#### ファイル名

- ページオブジェクト: `camelCase.ts`(例:`signInPage.ts`, `projectBoardPage.ts`- テストファイル: `camelCase.spec.ts`(例:`signIn.spec.ts`, `projectBoard.spec.ts`- ユーティリティ: `camelCase.ts`(例:`loginHelper.ts`, `dateFormatter.ts`- 設定ファイル: `camelCase.config.ts`(例:`playwright.config.ts`#### クラス名

- ページオブジェクト: `PascalCase`(例:`SignInPage`, `ProjectBoardPage`- テストヘルパー: `PascalCase`(例:`LoginHelper`, `TestDataGenerator`- カスタムフィクスチャー: `PascalCase`(例:`AuthFixture`, `DatabaseFixture`#### 変数名

- インスタンス: `camelCase`(例:`signInPage`, `projectBoardPage`- テストデータ: `camelCase`(例:`testUser`, `expectedResult`- 一時変数: `camelCase`(例:`currentDate`, `projectCount`#### 定数名

- グローバル定数: `SCREAMING_SNAKE_CASE`(例:`MAX_RETRY_COUNT`, `DEFAULT_TIMEOUT`- 環境変数: `SCREAMING_SNAKE_CASE`(例:`API_BASE_URL`, `TEST_USER_EMAIL`- テスト固有の定数: `SCREAMING_SNAKE_CASE`(例:`EXPECTED_ERROR_MESSAGE`, `VALID_PROJECT_NAME`#### メソッド名

- ページアクション: `camelCase`で動詞から始める(例:`clickLoginButton()`, `fillEmailField()`- ヘルパー関数: `camelCase`で動詞から始める(例:`generateRandomEmail()`, `formatDate()`#### セレクター変数名

- ロケーター: `camelCase`で要素の種類を含める(例:`loginButtonLocator`, `emailInputLocator`#### 命名の一般規則

- 略語は避け、明確な名前を使用する
- 単数形/複数形を適切に使用する(配列の場合は複数形)
- ブール値の変数は`is``has``should`などのプレフィックスを使用する
- 一時的な変数や不明確な名前(`temp`, `foo`, `bar`など)は避ける

### 17. 複数ユーザー操作時のtest.step記述ルール

- 1つのテストケース内で複数の操作ユーザー(例:会社権限オーナー、会社権限会社管理者、会社権限一般など)が登場する場合、各test.stepの説明文の先頭に、必ず「操作ユーザー」を明記すること。
  - 例:'【会社権限オーナー】案件担当一覧画面に遷移する'
  - 例:'【会社権限一般】案件検索で担当から除外された案件名で検索しても、検索結果に表示されないことを確認'
- 操作ユーザーの明記により、テストの可読性・保守性・デバッグ性を高めることを目的とする。
- 操作ユーザーの表記は、【会社権限オーナー】、【会社権限会社管理者】、【会社権限一般】 など、テスト内で役割が明確に分かる名称を用いること。

### 18. import文のパス指定

- import文では**絶対参照**を用いること。
  - NG例:
    ```typescript
    import { ExportTypeTabs } from './exportTypeTabs';
    ```
  - OK例:
    ```typescript
    import { ExportTypeTabs } from '@pages/exports/exportTypeTabs';
    ```

コーディングルールの設計思想

どのように記載したらAIがコーディングルールを理解し、それに応じたアウトプットをしてくれるのかという点を考慮して作成しています。

主に以下の3点を意識しています。

  • 自然言語で仕様を見える化
  • 責務を分ける
  • 優先度の設定

具体的に解説していきます。

自然言語で仕様を見える化

E2Eテストのテスト項目書を作成する場合、手順と期待結果を記載すると思います。

これはテストを実施する人が明確にどのような手順で実施してどういった結果が得られるのが適切なのかを理解した上でテストを実施するためです。

テストスクリプトにおいても同様であると考え、具体的な手順と期待結果を明確にすることで実際はどういった仕様が正しいのかをコード内に明文化しておくことができます。

以下のルールとして設定しています。

### 2. テストファイルの基本構造

- テストケースの初めに以下の形式でコメントを記載する:
  ```typescript
  /**
   * [テスト名]
   *
   * [テストの概要説明]
   *
   * テストの目的:
   * - [目的1]
   * - [目的2]
   * ...
   *
   * テスト手順:
   * 1. [手順1]
   * 2. [手順2]
   *    - [サブ手順2-1]
   *    - [サブ手順2-2]
   * ...
   *
   * 期待される結果:
   * - [期待結果1]
   * - [期待結果2]
   * ...
   *
   * 注意事項:
   * - [注意事項1]
   * - [注意事項2]
   * ...
   */
  ```

具体例

責務を分ける

これは、specとPO(Page Object)の責務を分けるということです。

弊社QAチームでは以下のように位置付けています。

  • POが行うもの
    • ロケーターの定義
    • 最小単位のブラウザ操作メソッドの定義
  • specが行うもの
    • POのメソッドを使用したブラウザ操作
    • 期待結果のアサーション

以下のルールとして設定しています。

### 4. ページオブジェクトパターンの使用

- 各画面の要素やアクションは、ページオブジェクトクラスとして実装する
- ページオブジェクトは pages/ ディレクトリに配置する
- 共通のアクションはページオブジェクトのメソッドとして実装する
- ページオブジェクトでは、expectはしない

特筆すべきところとしては、アサーションはspecに任せてPOではexpectをしないとしているところです。

expectをspecにまとめることで、テスト結果を確認する際にspecのみで手順と期待結果の全体像を把握することができます。

優先度の設定

これは主にロケーター取得時のPlaywrightのAPIの利用優先度です。

Playwrightにおけるロケーター取得APIの利用優先度は、プロダクトの改修による影響を受けにくい安定性の高い取得方法を選択するようにしています。

アクセシビリティ属性は比較的変更が少なく、class属性は変更が多いためこのような優先順位で設定しています。

(プロダクトによってはdata-testidの方が安定することもあると思いますので、セレクターの優先順位はカスタムが必要になるかと思います)

以下のルールとして設定しています。

### 6. セレクターの優先順位

- アクセシビリティ属性(role, name)を優先的に使用する
- placeholder 属性を次に使用する
- data-testid 属性を次に使用する
- テキストコンテンツを次に使用する
- class 属性は最終手段として使用する

こういった目的を持った明確なルールを設定することで、AIコーディングアシスタントによるコード補完・コードレビューがより期待値の高い提案をしてくれるようになります。

まとめ

まとめです。

コーディングルールの設定は以下のようなメリットがあります。

  • AIのコード補完精度が向上します。
  • AIのレビュー精度が向上します。
  • エンジニアのコード可読性が向上します。
  • レビュイー・レビュアー間の実装・レビュー指針の策定

ご紹介したPlaywrightを使ったE2Eテストにおけるコーディングルールは、以下のようなメリットがあります。

  • 仕様を自然言語でコードに添える(冒頭コメント)ことで、仕様と手順や期待結果の理解を助けます。
  • 責務分離(POM=操作、spec=検証)で“何をどこに書くか”が明確になり、テスト結果の確認も容易になります。
  • 優先度のあるルール(セレクタ/待機/命名)で、変更に強いテストが標準化できる。

今回ご紹介したコーディングルールが読者の皆さんの一助になれば幸いです。
もっとアルダグラムエンジニア組織を知りたい人、ぜひ下記の情報をチェックしてみてください!

アルダグラム Tech Blog

Discussion