Next.js 14 Server Componentのユニットテストについて。

Testing-library

以下を参考にServer Componentのユニットテストを作成します。 ただし、簡単なコンポーネントでしか確認していないので、コンポーネントによってはエラーになるかもしれません。

Server componentのテストでは、Server componentを関数として扱います。 以下の例ではrsc関数がServer Componentになります。

describe("Server Component Test", () => {
  test("Sample", async () => {
    const { asFragment } = render(await rsc(props));
    expect(asFragment()).toMatchSnapshot();
  });
});
テストでは Server component の関数にpropsを渡してasync関数として実行し、その結果をrenderに渡すことで、要素テストや スナップショップテストが行えます。

ただし、テストするServer Componentに子コンポーネントがある場合、子コンポーネントをMock化しないとエラーになる場合があります。 その場合は、jest.mockなどでMockにします。

jest.mock("子コンポーネントのパス", () => ({
  子コンポーネント関数名: () => <div>子コンポーネント</div>
}));

describe("Server Component Test", () => {
  test("Sample", async () => {
    const { asFragment } = render(await rsc(props));
    expect(asFragment()).toMatchSnapshot();
  });
});

サンプル・パラメータあり

URLクエリ ストリングが必要なServer Componentなページのサンプル。 これはURLクエリ ストリングのkeyに指定された文字列により表示する文字列が変わるページになります。

例 サンプルコンポーネント

注 import 'server-only' を "use server" にしてもテストは正常に実行できます。
import 'server-only';
import fs from "fs/promises";

export default async function FileTextPage(props: { searchParams: { key: string } }) {
  const { searchParams } = props;

  const list: Record<string, string> = {
    "dev": "src/app/sample/rsc/file-text/dev.txt",
    "stg": "src/app/sample/rsc/file-text/stg.txt",
  };
  const filePath = list[searchParams.key];
  let content = "Not exist key";
  if (filePath) {
    const buffer = await fs.readFile(filePath);
    content = buffer.toString();
  }

  return (
    <main className="w-full p-2 flex flex-col items-center">
      <div>
        <h1>サンプル・RSC・ファイルテキスト</h1>
      </div>
      <div className="p-4">
        <div>
          <div>テキストファイル</div>
          <textarea data-testid="text-file-content" className="bg-slate-100" defaultValue={content} readOnly />
        </div>
      </div>
      <div>
        <ul>
          <li><a className="underline" href="../">戻る</a></li>
        </ul>
      </div>
    </main>
  )
}

例 ユニットテストのサンプル

import FileTextPage from '@/app/sample/rsc/file-text/page';
import { render } from '@testing-library/react';

describe("FileTextPage test", () => {
  test("Test key dev", async () => {
    const { asFragment, findByTestId } = render(await FileTextPage({ searchParams: { key: "dev" }}));

    const element = await findByTestId("text-file-content") as HTMLTextAreaElement;
    expect(element.defaultValue).toBe("Test dev");
    expect(asFragment()).toMatchSnapshot();
  });

  test("Test key stg", async () => {
    const { asFragment, findByTestId } = render(await FileTextPage({ searchParams: { key: "stg" }}));

    const element = await findByTestId("text-file-content") as HTMLTextAreaElement;
    expect(element.defaultValue).toBe("Test stg");
    expect(asFragment()).toMatchSnapshot();
  });
});
これはServer Componentの要素テストとスナップショットテストが行えるサンプルです。