こんにちは、なかにしです。
今回はE2Eテストを試してみようと思います!
E2Eテストとは
フロントエンドテストの中でも最終段階で行われるテストです。
ユーザーの視点に立ち、アプリケーション全体の動作を確認することで、機能の一貫性と信頼性を保証します。
ライブラリ選定
有名どころだと、以下があります。
・Selenium
・Cypress
・playwright
今回はplaywrightを使用します。
playwrightの特徴は、以下です。
・Microsoft で開発およびメンテナンスが行われている、Node.js ベースの E2E テスト自動化フレームワーク
・マルチブラウザ対応(Chrome,Edge,Firefoxなど)
・モバイル用ブラウザも対応
・導入が簡単
CypressやSeleniumは使用したことがあったので、
playwrightも使ってみたいなーと思ったのが本音です。
テストを実施する
初期設定~導入
今回はNext.jsのアプリを対象として、E2Eテストを実施していきます。
まずは、Next.jsのアプリを用意します。
npx create-next-app
念のため、サーバが起動するか確認します。
npm run dev
初期画面が表示されることを確認したら、playwrightを導入していきます。
npm init playwright@latest
▽どのディレクトリに導入しますか?などを聞かれるので答えていきます。
▽ 終わりました。
Happy hacking!と言われたので楽しみます。
▽ testディレクトリ配下に、サンプル用のテストが書かれています。
3~8行目では、playwrightのページへ遷移し、Titleタグに「Playwright」という文字があるかを確認しているみたいですね。
テストの実行(サンプル)
それでは、先ほど作成されたサンプル用のテストを実行してみます。
以下コマンドで、testディレクトリ配下のテストをすべて実行します。
npx playwright test
▽ 終わったみたいです。
結果を見るには以下コマンドを打てと言われているので、確認します。
npx playwright show-report
▽ 結果が表示されました。
サンプルのコードでは、「has title」と「get started link」の2つのテストがありましたので、それが各ブラウザで実行されているようです。
今回はchromium、firefox、webkitで実行されています。
WebKitというと、Safariに使用されているHTMLレンダリングエンジンですね。
このあたりのブラウザ設定は、playwright.config.tsから変更することができます。
▽ 現在は chromium、firefox、webkitのみコメントアウトが外されています。
他のブラウザも、コメントアウトを外すことで実行できます。
// 35行目~
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
UIモードを使用すると、ブラウザを使用してテストの実行をしたり、ログやエラーの詳細を見ることができます。公式もオススメしている方法です。
npx playwright test --ui
▽ ブラウザが立ち上がり、手動実行できる
テストの実行(オリジナル)
基本的な使い方も分かったので、オリジナルのテストを書いていこうと思います。
今回は、以下の処理をテストしてみたいと思います。
・ボタンを押したらRoute HandlersにGETリクエストを投げる
・Route HandlersからJSON Placeholderにリクエストを投げ、データを取得する
▽ まずはボタンを用意します。
"use client";
import styles from "./page.module.css";
export default function Home() {
const requestToAPIRoute = async () => {
const response = await fetch("/api/getJsonData");
const data = await response.json();
console.log(data);
};
return (
<main className={styles.main}>
<div>
<button onClick={requestToAPIRoute}>
API Routeにリクエストするボタン
</button>
</div>
</main>
);
}
▽ Route Handlersを用意します。
import { NextResponse } from "next/server";
export async function GET() {
const response = await fetch("https://jsonplaceholder.typicode.com/posts");
const data = await response.json();
return NextResponse.json(data);
}
テストを用意します。
今回は、2種類用意しました。
テスト① API側をモック化し、ブラウザの挙動のみにフォーカスしたテスト
テスト② API側をモック化せず、実際の動きにフォーカスしたテスト
▽ テスト①
import { expect, test } from "@playwright/test";
test("APIボタンのテスト", async ({ page }) => {
// ページに移動
await page.goto("http://localhost:3000/");
// ボタンが配置されているか確認
const button = await page.locator("button");
await expect(button).toBeVisible();
// ボタンのテキストが正しいか確認
await expect(button).toHaveText("API Routeにリクエストするボタン");
// APIリクエストのモックを設定
await page.route("http://localhost:3000/api/getJsonData", (route) => {
route.fulfill({
status: 200,
body: JSON.stringify({ message: "Success" }),
});
});
// ボタンをクリック
await button.click();
// コンソールにAPIリクエストの結果が出力されるか確認
page.on("console", (msg) => {
if (msg.type() === "log") {
expect(msg.text()).toContain("Success");
}
});
});
▽ テスト②
import { expect, test } from "@playwright/test";
test("API Routeへリクエストが飛ぶかテスト", async ({ page }) => {
await page.goto("http://localhost:3000/");
// ボタンが配置されているか確認
const button = await page.locator("button");
await expect(button).toBeVisible();
// ボタンのテキストが正しいか確認
await expect(button).toHaveText("API Routeにリクエストするボタン");
// ボタンをクリック
await button.click();
// コンソールにAPIリクエストの結果が出力されるか確認
const consoleMessage = await page.waitForEvent("console");
if (consoleMessage.type() === "log") {
const messageText = consoleMessage.text();
// 実際に取得されるデータの中身をチェック
expect(messageText).toContain("est rerum tempore");
}
});
▽ どちらのテストも成功しました。
さいごに
各ブラウザで手軽にテストが実行できるのはいいですね!
導入やコマンドが簡単なのも個人的に好みです。
私はChromeをメインで使用しているので、個人アプリだとSafariやEdgeでの確認をつい怠ってしまうのですが、これだけ簡単に導入できると、ちょっと頑張って書いてみようと思えますね。
テストはアプリの品質に直結するので、ガツガツ書いて慣れていこうと思います。
今回はここまで!
Enjoy Hacking!!