Open4
非Vite環境にVitestによる単体テストを導入する
やりたいこと
- Next.js プロジェクトの単体テストを jest から vitest に乗り換える
- Reactコンポーネントのテスト
セットアップ
npx create-next-app --ts
必要なパッケージのインストール
npm i -D vitest @vitejs/plugin-react \
@testing-library/react @testing-library/jest-dom jsdom
各種設定
プロジェクトのルートディレクトリに vitest.config.ts
を作成し、以下を記入する
vitest.config.ts
/// <reference types="vitest" />
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
globals: true, // use `describe, it, test` without importing them
environment: "jsdom",
setupFiles: "./tests/setup.ts",
},
});
vitest の globals import はこのままだと TypeScript が構文エラーを起こすので tsconfig.json
も編集する
tsconfig.json
{
"compilerOptions": {
// ...
"types": ["vitest/globals"]
}
}
@testing-library/jest-dom
の .toBeInTheDocument()
などの matcher が全テストファイルで使えるよう、vitest.config.ts
で指名したセットアップファイル <rootDir>/tests/setup.ts
にインポートする
tests/setup.ts
import "@testing-library/jest-dom";
実践例
HeadlessUI の Listbox
で作ったセレクターコンポーネントをテストする
npm i @headlessui/react
components/SelectMenu.tsx
import { useState } from "react";
import { Listbox } from "@headlessui/react";
const options = ["Apple", "Banana", "Orange"];
export const SelectMenu = () => {
const [selectedFruit, setSelectedFruit] = useState(options[0]);
return (
<Listbox value={selectedFruit} onChange={setSelectedFruit}>
<Listbox.Button>{selectedFruit}</Listbox.Button>
<Listbox.Options>
{options.map((option) => (
<Listbox.Option key={option} value={option}>
{option}
</Listbox.Option>
))}
</Listbox.Options>
</Listbox>
);
};
components/SelectMenu.test.tsx
import { SelectMenu } from "./SelectMenu";
import { render, screen, fireEvent } from "@testing-library/react";
describe("SelectMenu test", () => {
it("should render and select", () => {
render(<SelectMenu />);
expect(screen.queryByText("Apple")).toBeInTheDocument();
expect(screen.queryByText("Orange")).not.toBeInTheDocument();
// Open the listbox and select 'Orange'
fireEvent.click(screen.getByText("Apple"));
fireEvent.click(screen.getByText("Orange"));
expect(screen.queryByText("Apple")).not.toBeInTheDocument();
expect(screen.queryByText("Orange")).toBeInTheDocument();
});
});
テスト実行
package.json
{
"scripts": {
// ...
"test": "vitest run"
}
}
npm run test
> test
> vitest run
RUN /home/elpnt/dev/mock/nextjs-vitest
√ components/SelectMenu.test.tsx (1)
Test Files 1 passed (1)
Tests 1 passed (1)
Time 1.93s (in thread 51ms, 3792.20%)