0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PlaywrightのAPIテストでトークンを使いまわす

Last updated at Posted at 2025-05-02

はじめに

Playwrightは、WebアプリケーションのE2Eテスト行うツールとしての印象が強いですが、APIテストも実行することができます。ここでは、APIテストを書く際にトークンを使いまわす方法について説明します。

実現したいこと

Web API のよくある仕様として、ログイン用のAPIにユーザー認証情報を送信して、トークンを取得し、そのトークンを使って他のAPIを利用するというものがあります。
ログイン以外のAPIのテストをするときに、毎回ログイン用APIを実行してトークンを取得し、その後本来実行したいAPIを実行するのは、テストの実行時間が長くなります。
ここでは、ログイン用APIを一番最初に実行してトークンを取得して、保存しておき、他のAPIのテストでそのトークンを使いまわすという方法を実現したいこととします。

実現方法

カスタムフィクスチャの定義

まず、「認証情報」「トークンを保存するファイル」「トークン」をPlaywrightのFixturesをとして定義します。ここで定義した「トークン」のフィクスチャをテストケースで共通して使用することになります。

fixtures.ts
import {test as base} from "@playwright/test";
import fs from "fs/promises";
import path from "path";
export {expect} from "@playwright/test";

type TestFixtures = {
  credentials: {
    username: string;
    password: string;
  };
  authFile: string;
  token: string;
};

export const test = base.extend<TestFixtures>({
  // 認証情報
  credentials: async ({}, use) => {
    const credentials = {
      username: process.env.USERNAME || "",
      password: process.env.PASSWORD || "",
    };
    await use(credentials);
  },
  // トークンを保存するファイル
  authFile: async ({}, use) => {
    const authFile = path.resolve(`.auth/user.json`);
    use(authFile);
  },
  // トークン
  token: async ({authFile}, use) => {
    const data = await fs.readFile(authFile, "utf-8");
    const {token} = JSON.parse(data);
    await use(token);
  },
});

テストケースを実装するときには@playwright/testからインポートしたtestではなく、ここで定義したtestをインポートして使用します。

ログイン → トークンの保存

ログイン用のAPIを実行して、トークンを取得し、トークンを保存する部分を実装します。

tests/login.setup.ts
import { test as setup, expect } from "./fixtures";

setup.describe("ログイン", () => {
  setup("トークンを発行する", async ({request, credentials, authFile}) => {
    const response = await request.post(`/login`, {
      data: {
        username: credentials.username,
        password: credentials.password,
      },
    });
    expect(response).toBeOK();
    const body = await response.json();
    expect(body).toHaveProperty("token");

    await fs.writeFile(authFile, JSON.stringify(body));
  });
});

ログイン時に必要な認証情報は、fixtures.tsで定義したcredentialsフィクスチャを使用して取得します。トークンは、authFileフィクスチャで指定したファイルに保存します。
なお、テストの前処理を実装するときは、慣例としてimport { test as setup }として、setupという名前でインポートします。

テストケースでのトークンの利用

保存したトークンをテストケースを利用します。
ここでは、トークンをAuthorizationヘッダーにセットして、APIを実行する例を示します。
テストを実装するファイル内で、test.useを使ってTestOptions.extraHTTPHeadersの値を変更します。これにより、このファイル内でのすべてのリクエストにAuthorizationヘッダーが追加されます。

tests/api.test.ts
import { test, expect } from "./fixtures";

test.use({
  extraHTTPHeaders: {
    Authorization: `Bearer ${token}`,
  },
});

test.describe("check application is active", () => {
  test("GET /api/health", async ({ request }) => {
    const response =
      await test.step("Act: request to API", async () => {
        return await request.get(`/api/users`);
      });

    await test.step("Assert: validate response", async () => {
      expect(response.status()).toBe(200);
      const body = await response.json();
      expect(body).toMatchObject({
        active: true,
      });
    });
  });
});

トークンの取得を一番最初に実行するように設定

テストケースを実行する前に、トークンを取得するように設定します。playwright.config.tsprojectsで、setupプロジェクトを定義し、apiプロジェクトのdependenciesに指定します。これにより、apiプロジェクトを実行する前に、setupプロジェクトが実行されます。

playwright.config.ts
import {defineConfig} from "@playwright/test";

export default defineConfig({
  projects: [
    {
      name: "setup",
      testMatch: "tests/*.setup.ts",
      teardown: "teardown",
    },
    {
      name: "api",
      testMatch: "tests/*.test.ts",
      dependencies: ["setup"],
    },
    {
      name: "teardown",
      testMatch: "tests/*.teardown.ts",
    },
  ],
});

ちなみに、teardownプロジェクトは、テストケース実行後に実行されるプロジェクトです。テストケース実行後に必要な処理があれば、ここに実装します。

tests/cleanup.teardown.ts
import { test as teardown } from "./fixtures";
import fs from "fs";
import fsAsync from "fs/promises";

teardown.describe("Clean files", () => {
  teardown("delete authorized token", async ({authFile}) => {
    if (fs.existsSync(authFile)) {
      await fsAsync.rm(authFile);
    }
  });
});

ここでは、テストケース実行後にトークンを保存したファイルを削除する処理を実装しています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?