前回の記事で、Playwrightのインストールと動作確認までを行いました。さっそくテストコードの自動生成機能を使って簡単にテストを作成したいところですが、現時点ではまだ生成されたコードをそのまま使うというよりも、人間が確認・修正する場合も多いと思います。

そこで今回は、シンプルなログイン・ログアウトのテストを使ってPlaywrightの基本的なテストの書き方をご紹介します。

テスト対象

Laravelプロジェクトの以下のようなログイン・ログアウト画面がテスト対象です。

ログイン画面はこのようなUIです。
Breezeログイン画面

テストでは、メールアドレス・パスワードを入力し、ログインボタンをクリックすると http://127.0.0.1:8000/user/diaryへ遷移することを確認します。

ログアウトは、このようなドロップダウンの要素になっています。
Playwrightログアウトテスト

ユーザー名をクリックするとドロップダウンが開きます。テストでは、ドロップダウンの中にある「ログアウト」をクリックし、ログアウトが成功してhttp://127.0.0.1:8000/loginへ遷移する、という一連の動作をテストします。

ログイン・ログアウトのテストコード

まず、ログイン・ログアウトのテストコード全体を見てみましょう。これは.tsのTypeScriptであることに注意してください。

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

test.describe('認証テスト', () => {
    test('ユーザーログイン', async ({ page }: { page: Page }) => {
        await page.goto('/login');

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

        // ログイン後の確認
        await expect(page).toHaveURL('/user/diary');

        // ユーザー名が表示されていることを確認
        await expect(page.getByRole('button', { name: 'テスト用ユーザー' })).toBeVisible();
    });

    test('ユーザーログアウト', async ({ page }: { page: Page }) => {
        // ログイン処理
        await page.goto('/login');

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

        // ログイン後の確認
        await expect(page).toHaveURL('/user/diary');

        // ユーザー名をクリックしてドロップダウンメニューを開く
        await page.getByRole('button', { name: 'テスト用ユーザー' }).click();

        // ログアウトをクリック
        await page.getByRole('button', { name: 'ログアウト' }).click();

        // ログアウト後の確認
        await expect(page).toHaveURL('/login');
        await expect(page.getByRole('button', { name: 'ログイン' })).toBeVisible();
    });
});

実行は以下のコマンドで行います。

$ npx playwright test auth

全体像がわかったところで、上のテストスクリプトにおけるPlaywrightの使い方を解説します。

Playwrightの構文

Playwrightの基本的な構文は、describe(テストをまとめるグループのようなもの)とtest(テストの1つ1つ)で構成されています。

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

test.describe('グループ', () => {
  test('テスト1', async ({ page }) => {
    // ...
  });

  test('テスト2', async ({ page }) => {
    // ...
  });
});

describeは必須ではないので、testだけを使っても問題ありません。

Playwrightのコマンド

次に、今回のテストで使用したPlaywrightのコマンドをご紹介します。

まずは画面遷移から。goto()を使用します。

await page.goto('http://127.0.0.1:8000');

コマンドの最初にあるawaitですが、こちらは処理の完了を待つために必要なものです。Playwrightの多くのコマンドは非同期処理なので、awaitを付けることで処理を待ってから安全に次に進むことができます。

そしてawaitの後ろにあるpageは、ページ操作のためのオブジェクトです。このpageオブジェクトの後にメソッドチェーンでgoto()などの各コマンドを繋ぐ形になっています。

次はフォーム入力です。以下のように「要素の取得」と「動作コマンド」を繋いで記述できます。

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

要素の取得はgetByLabel()というPlaywrightのコマンドを使い、引数に”メールアドレス”というラベルテキストを指定します。

E2Eテストの要素指定には、修正が発生しやすいCSSセレクタやXpathはできるだけ避け、ユーザーが実際に目にする要素を優先して指定することが推奨されており、そのための色々なロケータが用意されています

そしてfill()に入力したい内容を渡します。

次は、「ログインボタンをクリック」のようなクリック動作です。

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

getByRole()というコマンドを使用してまずは要素を取得しています。要素の役割(この場合はbutton)と、nameプロパティには、ラベルや要素のテキスト(この場合は”ログイン”)を渡します。

そして、click()を繋いでボタンをクリックします。

Playwrightのアサーション

次は検証コマンドです。「現在のURLがhttp://127.0.0.1:8000/loginである」ということを確認したい場合には、以下のように記述します。

await expect(page).toHaveURL('http://127.0.0.1:8000/login');

expect()の中に確認したい対象(この例ではpageオブジェクト)を渡し、toHaveURL()を繋いで期待するURLを指定します。

また、ログイン成功後の画面で「”テスト用ユーザー”というテキストを持つボタン要素が画面に表示されている」ということを確認したい場合、以下のように記述します。

await expect(page.getByRole('button', { name: 'テスト用ユーザー' })).toBeVisible();

こちらもexpect()から始まり、getByRole()に対象要素のbuttonと、nameプロパティにユーザー名を渡します。そしてtoBeVisible()を繋いでその要素が表示されているかを確認する、という流れです。

BaseURLを設定

テストで使用するURLは、playwright.config.tsにBaseURLとして指定しておくと便利です。

export default defineConfig({
・・・・・
  use: {
    /* Base URL to use in actions like `await page.goto('/')`. */
    baseURL: 'http://127.0.0.1:8000',
・・・・・

そうすると、goto()toHaveURL()で以下のように相対パスで書くことができます。

await page.goto('/login'); // http://127.0.0.1:8000/login にアクセスする

共通処理を関数へ切り出し

ログアウトのテストでは、ユーザーがログインしている状態を作るために、ログイン画面にアクセスしてメールアドレスを入力するなど、一連のログイン動作を記述しています。
今回はログイン・ログアウトの2つのテストだけなので必須ではありませんが、「ログイン状態を作るためのコード」を関数として切り出しておくと後々使いまわせますし、テストコードがすっきりします。

・・・・・
    test('ユーザーログアウト', async ({ page }: { page: Page }) => {
     // 関数を呼び出す
        await loginUser(page, 'test@example.com', 'password');

        // ここからログアウトのテスト
        ・・・・・
    });
});

// ログイン用関数
async function loginUser(page: Page, email: string, password: string): Promise<void> {
    await page.goto('/login');

    await page.getByLabel('メールアドレス').fill(email);
    await page.getByLabel('パスワード').fill(password);
    await page.getByRole('button', { name: 'ログイン' }).click();

    await page.waitForURL('/user/diary');
}

loginUser()という関数を作成し、そこにログインのためのコードを移しました。そしてログアウトのテストでloginUser()を呼び出しています。

今後共通化したいテストが増えてきたら別ファイルに切り出したり、Playwrightが提供している認証情報保持の機能などを使ってもいいと思います。

次回は、PlaywrightのCodegenを使ってテストコードを作成します。

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

By hmatsu