🍎

VitestとMSWでフロントエンドのテストを軽く作ってみる

2024/11/27に公開

はじめに

普段はjestを書いているのですが、vitestでフロントエンドのテストができるとのことで触ってみました。

テストする画面

テストしたい項目

検索ボタンクリック時の挙動で、

  • 何も入力しない場合は「品種を指定してね」と表示されること
  • 品種が見つからない場合は「指定の品種が見つかりません。」と表示されること
  • 写真のレスポンスが返ってきた場合は画像が表示されること

コード

  • MSWのハンドラー
    apiが呼び出された時に仮で返すデータを定義しています。
    正常系が一つあれば良いので、今回はbengが指定された時のデータだけ書いています。
    bengは日本語でベンガルネコでヒョウ柄とスリムな体型が特徴らしいです。(初めて知った)
import { http, HttpResponse } from "msw";

const cat: any = {
  beng: [
    {
      id: "beng",
      url: "http://example.com/beng.png",
    },
  ],
};
export const handlers = [
  http.get(`https://api.thecatapi.com/v1/images/search`, ({ request }) => {
    const url = new URL(request.url);
    const breed = url.searchParams.get("breed_ids");
    const catData = cat[breed ?? ""];
    if (catData) {
      return HttpResponse.json(catData, { status: 200 });
    } else {
      return HttpResponse.json([], { status: 404 });
    }
  }),
];
  • vitestファイル
    await user.click(buttonElement)でボタンクリック、
    await user.type(inputElement, "beng")で入力などユーザーの行動を指定します。
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, test } from "vitest";
import CatFinder from "../components/CatFinder";
describe(CatFinder, () => {
  test("何も入力しない場合は「品種を指定してね」と表示されること", async () => {
    render(<CatFinder />);
    const user = userEvent.setup();

    const buttonElement = screen.getByRole("button");
    await user.click(buttonElement);

    const pokemonName = screen.getByText("品種を指定してね");
    expect(pokemonName).toBeInTheDocument();
  });
  test("品種が見つからない場合は「指定の品種が見つかりません。」と表示されること", async () => {
    render(<CatFinder />);
    const user = userEvent.setup();
    const inputElement = screen.getByPlaceholderText("品種を入力");
    await user.type(inputElement, "human");

    const buttonElement = screen.getByRole("button");
    await user.click(buttonElement);

    const pokemonName = screen.getByText("指定の品種が見つかりません。");
    expect(pokemonName).toBeInTheDocument();
  });
  test("写真のレスポンスが返ってきた場合は画像が表示されること", async () => {
    render(<CatFinder />);
    const user = userEvent.setup();
    const inputElement = screen.getByPlaceholderText("品種を入力");
    await user.type(inputElement, "beng");

    const buttonElement = screen.getByRole("button");
    await user.click(buttonElement);

    const image = screen.getByRole("img");
    expect(image).toHaveAttribute("src", "http://example.com/beng.png");
    expect(image).toHaveAttribute("alt", "beng");
  });
});

こちらで実際にテストを実行すると全てパスして成功しました。
ファイルを保存したらテストが自動で実行されるのでいちいちコマンドを打つ手間がなくやりやすかったです。

テストカバレッジを見てみる

@vitest/coverageを使って、ファイルごとにテストの網羅率がどの程度あるかを出力してみます。
今回対象にしているCatFinder.tsxは100%ですが、handler.tsは16行目の分岐が網羅できておらず66%というようになっています。
大規模なプロジェクトになっていくと、ここ意識しすぎると開発のスピード結構落ちそうで参考程度にする程度の指標なのかなと思いました。

参考にさせてもらった記事

https://www.youtube.com/watch?v=vO7oJ_ugShY&t=1071s

NCDCエンジニアブログ

Discussion