Playwright、3記事目の今回はcodegenを使ってテストコードを生成します。codegenは、ブラウザ上での実際の操作を記録し、それをPlaywrightのテストコードに変換してくれる便利な機能です。VSCodeの拡張機能「Playwright Test for VSCode」も存在していますが、今回はシンプルにPlaywrightの機能のみを使用して作成します。

codegenでテストを作成する流れ

codegenでテストを作成する、大まかな流れは以下のようになっています。

  1. codegenでブラウザを操作してテストコードを生成
  2. 生成されたコードをVScodeなどのエディタにコピー
  3. テストコードの修正

前回の記事でも使用したLaravelプロジェクトを使って、ログイン〜ログアウトをこの流れでテストコードにしてゆきます。

codegenで動作を記録

Playwrightはすでにインストール済みの状態から始めます。インストールがまだの方はセットアップの記事をご確認ください。

以下のコマンドでcodegenを起動します。コマンドの最後に対象のURLを書いておくと、URLへ遷移した状態で始めることができます。この例ではローカルのポート8000を使用していますが、実際の環境に合わせてURLを変更ください。

$ npx playwright codegen http://127.0.0.1:8000

コマンドを実行すると、ブラウザとPlaywrightインスペクターウィンドウが立ち上がりました。

この状態から実際にブラウザを操作してテスト対象の動作を行うことで、テストコードが生成されてゆきます。例えば、メールアドレス入力欄に”test@example.com”と入力してみると、インスペクターの方にもコードが出力されます。

ですが、想像していたより多くコードが生成されたようなので内容を確認してみます。以下が実際のコードです。

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

test('test', async ({ page }) => {
  await page.goto('http://127.0.0.1:8000/login');
  await page.getByRole('textbox', { name: 'メールアドレス' }).click();
  await page.getByRole('textbox', { name: 'メールアドレス' }).press('Eisu');
  await page.getByRole('textbox', { name: 'メールアドレス' }).fill('test@example.com');
});

「メールアドレスを入力」という動作だけのつもりだったのですが、入力欄をクリックする動作やキーボードの英数キーをクリックした動作までコードに反映されてしまっているようです。インスペクタ上ではコードを削除・修正はできないので、こちらはエディタにコピーしてから修正が必要になります。

肝心のメールアドレス入力は、最後の行にawait page.getByRole('textbox', { name: 'メールアドレス' }).fill('test@example.com');として、ちゃんと生成されていますね。

codegenでアサートを追加

テストジェネレーターで追加できるアサーションは以下の3種類となっています。

  • 左端の目のアイコン assert visibility:要素がページ上で可視状態であることを確認
  • 中央”ab”のアイコンassert text:要素が指定されたテキストコンテンツを含んでいることを確認
  • 右端テキストボックスアイコンassert value:入力要素が特定の値を持っていることを確認

今回のテストでは、assert text・assert visibilityの2つを使用します。まずはassert textで、「ログイン成功後にアカウント名が表示されていること」のアサーションを作成します。

①”ab”アイコンをクリックし、②確認対象の要素(アカウント名)をクリックします。すると以下のように入力ボックスが表示されるので、テスト用の文字列を変更したい場合はこちらで修正が可能です。問題なければ③のチェックマークをクリック

assert textでは、以下のようにtoContainText()を使用したアサーションが生成されます。

await expect(page.getByRole('button')).toContainText('テスト用ユーザー');

続いてassert visibilityで、「ログアウト後に”ログインボタン”が表示されていること」のアサーションを作成します。

①目のアイコンをクリックし、②確認対象の要素(ログインボタン)をクリックします。

assert visibilityでは、以下のようにtoBeVisible()を使用したアサーションが作成されます。

await expect(page.getByRole('button', { name: 'ログイン' })).toBeVisible();

本当は「遷移後のURLが期待通りであるか」のアサーションも作成したかったのですが、現状テストジェネレーターにはURLを直接アサートするための機能はありませんでした。

基本的に、ページ上の要素(ボタン、テキスト、入力欄など)に対して「表示されているか」「特定のテキストを含んでいるか」などの検証のみとなっているようなので、URLのアサートを追加したい場合は手動で追記する必要があります。

テストコードの作成が完了したら

テストコードをひととおり作成したら、インスペクタの赤丸ボタンをクリックして記録を停止させます。コピーボタンでコードをコピーしたら、あとはエディタに貼り付けて修正を行います。

不要なコードを削除

codegenが生成してくれたコードは以下の通りです。

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

test('test', async ({ page }) => {
  await page.goto('http://127.0.0.1:8000/login');
  await page.getByRole('textbox', { name: 'メールアドレス' }).click();
  await page.getByRole('textbox', { name: 'メールアドレス' }).press('Eisu');
  await page.getByRole('textbox', { name: 'メールアドレス' }).fill('test@example.com');
  await page.getByRole('textbox', { name: 'メールアドレス' }).press('Tab');
  await page.getByRole('textbox', { name: 'パスワード' }).fill('password');
  await page.getByRole('button', { name: 'ログイン' }).click();
  await expect(page.getByRole('button')).toContainText('テスト用ユーザー');
  await page.getByRole('button', { name: 'テスト用ユーザー' }).click();
  await page.getByText('ログアウト').click();
  await expect(page.getByRole('button', { name: 'ログイン' })).toBeVisible();
});

先述の通りキーボードを押した時の動作などもコードになってしまっているので、これらの不要な行を削除します。

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

test('test', async ({ page }) => {
  await page.goto('http://127.0.0.1:8000/login');

  //ログイン動作
  await page.getByRole('textbox', { name: 'メールアドレス' }).fill('test@example.com');
  await page.getByRole('textbox', { name: 'パスワード' }).fill('password');
  await page.getByRole('button', { name: 'ログイン' }).click();

  //ログイン後のアサーション
  await expect(page.getByRole('button')).toContainText('テスト用ユーザー');
  
  //ログアウト動作
  await page.getByRole('button', { name: 'テスト用ユーザー' }).click();
  await page.getByText('ログアウト').click();
  
  //ログアウト後のアサーション
  await expect(page.getByRole('button', { name: 'ログイン' })).toBeVisible();
});

これでスッキリしました。まだここからBaseURLを使用する形に修正したり、URLアサーションの追加などを適宜行う必要はありますが、叩き台を作成してくれるので使い慣れればテストの作成時間を短縮できそうです。

自分で書いたコードとcodegenが生成したテストコードの違い

今回codegenが生成してくれたコードを見て、前回私が手動で作成したコードとの違いの中で気になったのはロケータの選定です。

自作のコードでは、getByLabel()を使用しました。

await page.getByLabel('メールアドレス').fill('test@example.com');

一方codegenは、getByRole()を使用しています。

await page.getByRole('textbox', { name: 'メールアドレス' }).fill('test@example.com');

getByLabel()は、getByRole()に比べてコードが簡潔に書けます。またラベル要素を指定することで「フォーム要素のテストである」という意図がわかりやすい、といったメリットがあります。

getByRole()は、こちらも「”メールアドレス”というテキストボックスが対象」という意図がわかりやすいです。getByLabel()との一番の違いは、フォーム要素に限らず様々な要素指定に使える汎用性が高いロケーターである、という点です。

どちらもPlaywrightの推奨ロケータですが、今後codegenに限らずAIでE2Eテストを作成する機会が増えることになるかと思いますので、手動作成とAIが生成したコードの差異を出さないためにも生成コードに寄せていったほうがいいのかな、と思いました。

メルマガ購読の申し込みはこちらから。

By hmatsu