以前にご紹介したPlaywrightテスト作成の記事では、一連のログイン動作を共通処理として関数化し、使いまわせるようにしました。この方法ではコード上はすっきりしますが、ログイン処理がテストごとに実行される点は同じです。

そこで今回は、PlaywrightのAuthenticationを使って「認証状態」自体を使いまわせるようにしてゆきます。毎回ログイン処理を行う必要がなくなるので、テスト時間の短縮が期待できます。

関連記事:PlaywrightでE2Eテストを自動化 (2)ログイン・ログアウト

Authenticationとは

Authenticationは、認証済みのブラウザ状態をファイルに保存し、各テストの実行時に読み込ませることで「ログイン済み」の状態からテストを開始できる仕組みです。認証を使用する流れは、簡単に書くと以下のようになっています。

  1. セットアップ用ファイル(auth.setup.ts)を作成し、playwright.config.tsに定義する
  2. テスト開始前に1のセットアップが実行されると、ログイン処理と共にuser.jsonにログイン状態が保存される
  3. テストのstorageStateオプションにuser.jsonを指定することで、ブラウザが認証済みの状態で起動する

注意点は、「サーバー側のセッションデータ」は共有されないということです。PlaywrightのAuthenticationは「ブラウザのクッキー」を保存・再利用する仕組みのため、ログアウトのようなサーバー側のセッションを削除する操作を行うと、同じクッキーを使っている他のテストでも認証が切れてしまいます。

この問題を避けるため、ログアウトのテストでは他のテストとアカウントを分けることが推奨されています。

Authenticationの設定方法

ではAuthenticationを設定します。今回は、任意のテストファイルやテストグループに対してのみ認証を適用する形にしたいので、それを目標に進めてゆきます。

playwright/.auth ディレクトリの作成

認証状態を保存するuser.jsonファイル自体は、セットアップ実行時に自動で作成されます。ですがファイルを保存するディレクトリ(Playwrightではplaywright/.auth推奨)は作成されないため、無い場合は以下のコマンドで事前に作成します。

また、機密情報を持つファイルをリポジトリに含めないよう、.gitignoreにディレクトリを追加します。

$ mkdir -p playwright/.auth
$ echo $'\nplaywright/.auth' >> .gitignore

セットアップ用ファイルを作成

次に、セットアップファイルを作成します。auth.setup.tsという名前のファイルを作成し、以下のように記載します。

import { test as setup } from '@playwright/test';

const authFile = 'playwright/.auth/user.json';

setup('認証情報を保存', async ({ page }) => {

    await page.goto('/login');

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

    // ログイン成功を確認
    await page.waitForURL('/user/diary');

    // 認証状態を保存
    await page.context().storageState({ path: authFile });
});

ログイン動作自体は通常のPlaywrightのブラウザ操作と同じ書き方です。重要なのは、コードの最後のpage.context().storageState({ path: authFile });の箇所です。user.jsonのパスを渡し、認証状態を保存しています。

Playwrightのドキュメントによると、ログイン動作後すぐに認証状態を保存するのではなく、上のコードのawait page.waitForURL('/user/diary');のように、確実にログインしたことを確認するコードを挟んだ後で保存することがおすすめのようです。

configファイルに定義

playwright.config.tsに、作成したセットアップファイルを定義します。

・・・・・
  projects: [
    {
      name: 'setup',
      testMatch: /.*auth\.setup\.ts/,
    },
    {
      name: 'chromium',
      dependencies: ['setup'], // 先にsetupを実行
      use: { ...devices['Desktop Chrome'] },
    },
・・・・・

name: 'setup'でセットアップ用のプロジェクトを定義し、dependencies: ['setup']では、chromiumのテスト実行前にsetupを実行することを定義しています。dependencies: ['setup']はブラウザごとに記述する必要があるので、firefoxやwebkitなども使用する場合はそちらにも追記してくださいね。

テストファイルでuseする

最後に、認証ファイルを任意のテストで読み込みます。以下のようにtest.use()を使い、認証ファイルへのパスを記載します。

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

test.describe('ログイン済みユーザーを使ったテスト', () => {

    test.use({ storageState: 'playwright/.auth/user.json' }); //認証ファイルのパスを指定

    test('ダッシュボードの表示', async ({ page }: { page: Page }) => {

        // すでにログイン済みの状態からテストを開始できる
        await page.goto('/user/diary');
・・・・・
    });
});

test.use()の記述位置ですが、個々のtest()の中に書くとエラーとなりますので、上の例のようにtest()に配置してください。そうすると、test.describe()のグループ全体、またはそのテストファイル全体に認証状態が適用されます。

テストの実行時

先ほどのauth.setup.tsでは、メールアドレス・パスワードなどログインに使う情報を、環境変数(process.env)として取得していました。そのためテスト実行時には、ターミナルで以下のコマンドを実行して環境変数をセットしてください。

$ export TEST_EMAIL=test@example.com; export TEST_PASSWORD=password
$ npx playwright test

exportコマンドはMacやLinux環境を想定しています。Windowsをお使いの場合は、ご利用のシェルに合わせて適宜コマンドを読み替えて実行ください。

全体を認証済みとし、特定のテストだけ認証状態をリセットする場合

先ほどの例では任意のテストでのみ認証状態を使用しましたが、テスト全体はログイン済みの状態とし、一部のテストでのみ未ログイン状態としたい場合もあるかと思います。

そのような場合は、以下のようにplaywright.config.tsで全体に認証情報を設定します。適用したいブラウザ(ここではchromium)プロジェクトのuseオプションに、storageStateとしてuser.jsonファイルを追加します。

・・・・・
  projects: [
    {
      name: 'setup',
      testMatch: /.*auth\.setup\.ts/,
    },
    {
      name: 'chromium',
      dependencies: ['setup'],
      use: {
        ...devices['Desktop Chrome'],
        // 認証データを使用
        storageState: 'playwright/.auth/user.json', // ここを追加
      },
    },
・・・・・

そして認証を使いたくないテストファイルでは、以下のようにtest.use()storageStateの状態をクリアします。

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

test.describe('未ログインユーザーのテスト', () => {

    test.use({ storageState: { cookies: [], origins: [] } });

    test('ログイン成功', async ({ page }: { page: Page }) => {
・・・・・
    });
});

テスト時間が変わるのか確認

では、Authenticationを使うことでテスト実行時間に影響があるか確認してみます。対象のテストにはログイン処理を実行する箇所が8箇所あり、これらをAuthenticationを使う形に修正しました。比較するにはテスト数が少ないですが、これでやってみます。

まずはAuthentication導入前、ログイン処理が8箇所あるテストを実行してみます。

$ npx playwright test

Running 11 tests using 4 workers
・・・・・
  11 passed (8.6s)

8.6秒でテストが完了しました。

次に、Authenticationによる認証状態を使用したコードでテストを実行します。

$ npx playwright test

Running 11 tests using 4 workers
・・・・・
  11 passed (5.9s)

今度は5.9秒でテストが完了しました。念のためテストをそれぞれ複数回実行しましたが、後者のテストのほうが常に2秒以上短い時間でテストが完了する結果となりました。

たった8箇所に認証状態を適用しただけで2〜3秒テスト時間が短縮できたので、テスト数が多ければ恩恵は大きいと思います。なによりログイン部分を省略してすっきり書けますので、Playwrightを使用している方はぜひ試してみてください。

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

By hmatsu