VSCodeでPlaywrightによるE2Eテスト自動化をはじめてみよう

今年8月にリリースされたPlaywrightのVSCode拡張を今更ながら深堀りして触ってみました。
本記事では、VSCode上でPlaywrightを使ったテストの生成・実行・デバッグ・結果確認を一通り実行する方法を紹介します。
PlaywrightないしEnd-to-End(E2E)テスト自動化がだいたいどんなものかを知りたい人に見てもらえれば嬉しいです。

## はじめに

### Playwrightとは

https://playwright.dev/

Microsoftが開発しているE2Eテスト自動化ライブラリです。
この分野ではSeleniumやCypressといったライブラリが先行していましたが、近年ではビッグテックならではの開発力を背景にPlaywrightが急速にシェアを拡大しているように思います。
事実これらを使い比べてみてもPlaywrightが圧倒的に使いやすく、これからE2Eテスト自動化に取り組んでみたい方にはまず最初にオススメしたいツールになっています。

PlaywrightはNode.js(TypeScript/JavaScript)、Python、Java、.NETなど、複数の言語で利用可能です。
開発の中心となっているのはNode.js版で、VSCode拡張においてもNode.js版のPlaywrightがバックエンドで走る仕様になっています。

### Playwright VSCode拡張

PlaywrightのVSCode拡張は2022年8月に発表されたVer 1.25から導入されました。
それから2022年11月のVer 1.28アップデートでも更新が加えられるなど、今後ともに頻繁にアップデートされることが期待されます。
VSCodeも当然ながらMicrosoft社のプロダクトなので、もしかしたらPlaywrightはVSCodeと一緒に使うものだと公式側からもプッシュされていくのかもしれませんね。

https://playwright.dev/docs/release-notes

## 環境構築

### 前提
下記のインストールは終わっているものとします。
- Node.js
- Visual Studio Code

### インストール

**1.VSCodeプラグイン「Playwright Test for VSCode」をインストールします。**

https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright

**2.プロジェクトフォルダをVSCodeで開きます。**

**3.VSCodeのコマンドパレットを開き、下記コマンドを実行します。**
```shell
>Test: Install Playwright
```

**4.対話式ダイアログにて下記事項を設定し、「OK」を押します。**
- インストールするブラウザ(Chromium, Firefox, Webkit)
- TypeScriptかJavaScriptかの選択
- GitHubActions flowを追加するか

**5.インストールが完了するまで待ちます。**
インストールが完了すると、フォルダ内に設定ファイルやサンプルスクリプトなどが生成されます。
![スクリーンショット 2022-12-12 17.07.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527184/40cace43-9a19-0c93-a41f-23564a3765b8.png)

## テストの実行・結果確認・デバッグ

デフォルトで作成されるサンプルスクリプトを用いて、基本の使い方であるテストの実行とデバッグを試してみましょう。

### テストの実行・結果確認

**1.左サイドバーに「テスト」という項目があるのでクリックし、テストバーを開きます。**

「テストエクスプローラー」セクションには`/tests/`内に存在する全てのテストケースが表示されます。
またサイドバー下部に「PLAYWRIGHT」のセクションがあることも確認しておいてください。
![スクリーンショット 2022-12-12 17.48.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527184/7d036e72-e49f-5d92-84d8-e761189c9198.png)
なお、テストエクスプローラーにてどのフォルダを参照するかは`playwright.config.ts`内、`testConfig.testDir`にて指定されます。
https://playwright.dev/docs/api/class-testconfig#test-config-test-dir

**2.テストエクスプローラーに表示されているテスト`homepage has title and links to intro page`上で右クリックし、「テストに移動」をクリックします。**
当該メソッドが含まれるスクリプト(`tests/example.spec.ts`)が開かれます。

**3.再びテスト`homepage has title and links to intro page`上で右クリックし、「テストの実行」をクリックします。**
Chromiumがヘッドレスモードで起動しテストが実行されます。
テストが成功すると、テストエクスプローラー及びスクリプト内のメソッド部分にPassマーク(✔)が付きます。

なお、 テストサイドバーのPLAYWRIGHTセクションの「Show browsers」にチェックを入れるとブラウザがheadedモードで起動します。

**4.実行結果としてHTMLレポートが自動作成されることを確認します。**
実行後に`playwright-report/index.html`が作成されるので、これをブラウザで開けばテストレポートを確認することが出来ます。
このレポーターには複数の種類があり、`playwright.config.ts`内で設定されています。詳細は下記ページを参照ください。

https://playwright.dev/docs/test-reporters

### テストのデバッグ

**1. スクリプト`tests/example.spec.ts`にて、停止させたい行にブレークポイントを置きます。**
今回は例として10行目にブレークポイントを置きました。
```typescript
  const getStarted = page.getByRole('link', { name: 'Get started' });
```

**2.テストエクスプローラーにて、メソッド`homepage has title and links to intro page`上で右クリックし、「テストをデバッグ」をクリックします。**
Chromiumがheadedモードで起動し、ブレークポイントまで当該テストが実行されることを確認してみましょう。

またこの際、VSCodeのサイドバーは「実行とデバッグ」に遷移し、デバッガが起動していることも同時に確認してください。
デバッガを利用することで変数の中身を見ながらステップ実行などを行うことができます。
![スクリーンショット 2022-12-12 17.52.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527184/b1bf29e9-c6bc-c2ca-2ec5-79ebc521ff71.png)

**3.テストサイドバーに戻り、PLAYWRIGHTセクションのところで「Pick locator」をクリックします。**
開いたブラウザ上で、要素をクリックするとその箇所のロケータを取得するメソッドが表示されます。
またVSCode上でそのメソッドのコピーもできます。

![スクリーンショット 2022-12-13 13.26.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527184/da750ea2-47ca-605e-2164-a9711b5d1ecf.png)

#### 補足: 実行ブラウザのプロファイル変更

デフォルトではChromium単体でのテスト実行・デバッグの設定になっていますが、
テストエクスプローラー横の実行ボタン（2重になった▷印）から「規定のプロファイルの選択」を押すことで実行ブラウザの変更をすることができます。
![スクリーンショット 2022-12-12 17.55.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527184/9eb28510-a655-5e64-a2f4-a4998cbf4e2b.png)
ここでは単一のブラウザを指定することも、複数のブラウザを指定することもできます。複数のブラウザを設定するとテストが並列で実行され、workerがその分増加することになります。
テスト出力を見たい場合はテストサイドバーのPLAYRWRIGHTセクションの「Reveal test output」をクリックすれば実行結果のアウトプットを確認できるほか、HTMLレポート上でもどのブラウザで実行されたかを確認することができます。

## テストの作成・編集

では自分でテストを作ってみようと思います。
PlaywrightにはTest Generatorというブラウザ上の動作を記録してテストを生成できる機能があるので、これを利用してベースを作ってから個別に編集を入れるのがオススメです。

### テストの作成

**1.テストサイドバーのPLAYWRIGHTセクションのところで「Record new」をクリックします。**
空のタブでブラウザが起動するので、自動化したいサイトURLを入力します。

今回はテスト自動化練習用サイトであるHOTEL PLANISPHERE(`https://hotel.testplanisphere.dev/`)を自動化してみようと思います。

https://hotel.testplanisphere.dev/

**2.ブラウザ上で操作すると、スクリプトが勝手に追加されていきます。**
好きなテストケースを作ってみてください。記録が完了したらブラウザを閉じます。

**3.余計な操作まで記録されるので、不要な行を削除します。**
自分の場合は削除による整理を行ったあと以下のようになりました。

```javascript
import { test, expect } from '@playwright/test';

test('test', async ({ page }) => {
  await page.goto('https://hotel.testplanisphere.dev/');
  await page.getByRole('link', { name: 'トップページへ' }).click();
  await page.getByRole('link', { name: '宿泊予約' }).click();
  const [page1] = await Promise.all([
    page.waitForEvent('popup'),
    page.locator('.card-body > .btn').first().click()
  ]);
  await page1.getByLabel('宿泊日 必須').click();
  await page1.getByRole('link', { name: '24' }).click();
  await page1.getByLabel('宿泊数 必須').fill('1');
  await page1.getByLabel('人数 必須').fill('1');
  await page1.getByLabel('朝食バイキング').check();
  await page1.getByLabel('氏名 必須').fill('サンタ');
  await page1.getByRole('combobox', { name: '確認のご連絡 必須' }).selectOption('no');
  await page1.locator('[data-test="submit-button"]').click();
  // 価格をアサーション(?)
  await page1.getByRole('heading', { name: '合計 9,750円（税込み）' }).click();
});
```

ここで注意したいのが最後の行です。
ここにはアサーションを挿入したかったので代わりに一度要素をクリックしてみたところ、nameによる指定がされてしまい、金額が変わった際に要素を取得できなさそうなセレクタの指定方法になってしまいました。
こういったところはちゃんとDOMを確認しながら書き換えてあげる必要がありますね。

**4.テストとしての形を整えます。（アサーションを追加・テスト名を変更・コメントを挿入など）**
アサーションの基本構文は`expect(要素).toメソッド("期待値")`です。詳細は以下のページを確認してください。

https://playwright.dev/docs/test-assertions

最終調整の結果、僕の場合は以下のようなスクリプトになりました。

```javascript
import { test, expect } from '@playwright/test';

test('宿泊予約の価格が合っているか確認', async ({ page }) => {
  await page.goto('https://hotel.testplanisphere.dev/');

  // 日本語トップをクリック
  await page.getByRole('link', { name: 'トップページへ' }).click();

  // 宿泊予約ページをクリック
  await page.getByRole('link', { name: '宿泊予約' }).click();

  // 「お得な特典付きプラン」をクリック
  const [page1] = await Promise.all([
    page.waitForEvent('popup'),
    page.locator('.card-body > .btn').first().click()
  ]);

  // 予約フォーム入力
  // 宿泊日を当月の24日に設定(記事執筆時点で2022年12月)
  await page1.getByLabel('宿泊日 必須').click();
  await page1.getByRole('link', { name: '24' }).click();
  
  // 宿泊数を入力
  await page1.getByLabel('宿泊数 必須').fill('1');

  // 人数を入力
  await page1.getByLabel('人数 必須').fill('1');

  // オプションを設定
  await page1.getByLabel('朝食バイキング').check();

  // 氏名を入力
  await page1.getByLabel('氏名 必須').fill('サンタ');

  // 確認の連絡の入力
  await page1.getByRole('combobox', { name: '確認のご連絡 必須' }).selectOption('no');

  // 予約内容を確認ボタンをクリック
  await page1.locator('[data-test="submit-button"]').click();

  // 価格をアサーション
  await expect(page1.locator('id=total-bill')).toContainText('9,750円');
});
```

アサーションのロケータはIDによる指定を採用しています。
個人的には以下の優先順でロケータを使っています。これはPlaywright公式の思想にも近いと思います。

1. テスト用のIDを別途付与する
1. role, rabelのようなユーザがぱっと確認できるようなロケータを使う (Test Generatorで生成されるステップはこれ)
1. (開発と共用の)IDを使う
1. CSS/XPathを使う

> CSS and XPath are not recommended as the DOM can often change leading to non resilient tests. Instead, try to come up with a locator that is close to how the user perceives the page such as role locators or define an explicit testing contract using test ids.

https://playwright.dev/docs/locators


**5.テストを実行し、無事パスしたら完了です！**
HTMLレポートではステップごとの結果参照ができます。
個人的にはコメントなしでも分かるスクリプトを書くのが好きですが、HTMLレポートをパッと第三者に投げる用途が考えられる場合はコメントをしっかり書くのも1つの方針かもしれませんね。
![_Users_Yabusaki_Develop_Playground_playwright_playwright-report_index.html (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527184/8d6e349f-d174-c460-04be-04865b7f9e8d.png)
なお、上記スクリプトだとブラウザによっては日付の入力仕様の関係でfailする可能性があること、
また2022年12月以降に実行すると恐らく金額違いでfailすること、
そしてその月の25日以降に実行すると恐らく日付指定ができずにエラーになることにご注意ください。
~~サンタさんは25日以降には動かなくなってしまうんや~~
(こういうときは日付の値を直で入力するのが一般的な方法です)

### テストの編集

Test Generatorを使って、テストを途中から記録することも可能です。
VSCode上でステップを挿入したい行をクリックしたあと、テストサイドバーのPlaywrightセクションで「Record at cursor」をクリックするとブラウザが起動し、再びステップを挿入できるようになります。
なおVSCode上ではデバッガと組み合わせて利用することはまだできないようです。

## まとめ

ここまでで一通りの使い方は攫うことができました。
ね、簡単でしょ？
・・・と言えるかは人に依るかと思いますが、Seleniumとブラウザの開発者ツールを使いながらテストを書いていた時代に比べると楽になってきたものだと感じずにはいられません。

今回の記事では紹介できませんでしたが、Playwrightにはアクションごとの経過時間などのログを取れる[Trace Viewer](https://playwright.dev/docs/trace-viewer-intro)や、ビルトインで利用できる[画像比較機能](https://playwright.dev/docs/test-snapshots)など、便利な機能が沢山あります。

本記事が皆様の勉強のキッカケになれば幸いです！