Vite + Vitest の環境を設定してみた

Vite + Vitest の環境構築や使い方をハンズオン形式で実践して整理した際の記事です。
2024.04.12

はじめに

こんにちは。アノテーションの及川です。

業務の中で、Vite + Vitest のコードを確認する機会があり、その構成や使い方を学ぶためハンズオン形式で実施したことを整理しました。

Vite

Vite の詳細はこちらからご参照ください。

概要

  • Vite(フランス語で「速い」を意味する)は、高速な開発サーバーとビルドツールを提供します。
  • Vite は従来のモジュールバンドラーとは異なり、開発時にはネイティブESモジュールを利用してブラウザで直接モジュールをインポートします。これにより、開発時の起動が非常に高速になります。

特徴

  • 高速なコールドスタート
    • Vite は開発環境でのページリロードを必要とせず、依存関係のプリバンドリングによってプロジェクトの起動を高速化します。
  • ホットモジュールリプレースメント (HMR)
    • ファイルが変更されたときに、その変更を即座にブラウザに反映させることができ、開発プロセスをスムーズにします。
  • ビルド最適化
    • Vite は開発中はモジュールバンドラーを使用しませんが、本番環境用のビルドでは Rollup を使用して最適化されたアセットを生成します。これにより、本番環境でのパフォーマンスが向上します。

Vitest 概要

Vitest の詳細はこちらからご参照ください。

概要

  • Vitest は、Vite エコシステムの一部として開発されたモダンなテストランナーおよびアサーションライブラリです。
  • Vite の高速なモジュール解決とトランスパイル機能を利用して、テストの実行を高速化します。
  • Jest に似たAPIを提供するため、Jestからの移行も比較的簡単です。

特徴

  • 高速なテスト実行
    • Vite のインフラストラクチャを利用して、テストの起動と実行を高速化します。
  • Jest 互換
    • Jest と同様の API を提供し、既存の Jest テストを Vitest に容易に移行できるように設計されています。
  • ネイティブ ESM サポート
    • ネイティブ ES モジュールをフルサポートしており、トランスパイルを必要とせずにテストを実行できます。

Vite インストール

「React」 + 「TypeScript + SWC」 の構成でインストールします。

SWC は、次世代の高速開発ツールのための拡張可能な Rust ベースのプラットフォームであり、コンパイルとバンドルの両方に使用できます。

最新の JavaScript 機能を使用して JavaScript / TypeScript ファイルを受け取り、すべての主要なブラウザーでサポートされる有効なコードを出力します。

% node --version
v20.11.0

% npm create vite@latest vitest-setup-test
Need to install the following packages:
create-vite@5.2.3
Ok to proceed? (y) # ← 'y'で良いのでそのまま Enter
? Select a framework: › - Use arrow-keys. Return to submit.
    Vanilla
    Vue
❯   React
    Preact
    Lit
    Svelte
    Solid
    Qwik
    Others

✔ Select a framework: › React
? Select a variant: › - Use arrow-keys. Return to submit.
    TypeScript
❯   TypeScript + SWC
    JavaScript
    JavaScript + SWC
    Remix ↗

✔ Select a framework: › React
✔ Select a variant: › TypeScript + SWC

Scaffolding project in /Users/anote.taro/dev/Introduction-to-React-Testing-with-Vitest/vitest-setup-test...

Done. Now run:

  cd vitest-setup-test
  npm install
  npm run dev

% cd vitest-setup-test
% npm install

added 163 packages, and audited 164 packages in 9s

38 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

% code . # (任意)Visual Studio Code エディタで開く場合は下記コマンド入力して Enter

【任意】インポート(import) パスエイリアス設定

下記の例のように、別のディレクトリ配下のファイルを import する際に、"相対パス"で指定するところを、"@/~"の形で指定することができます。

階層が深いディレクリ構成になってくると、../../../../../~のように指定するとコードを追うのが大変になったり、パス指定を誤ったりするため、"@" マークを基点に指定することが可能になります。

Vitest の環境構築の過程でついでに設定できるので、よろしければ是非設定をお試しください。

import Book from "./components/Book.tsx" // 相対パス
↓
import Book from "@/components/Book.tsx" // @マークを基点としたパス指定

下記部分を tsconfig.jsonに追記してください。

下記はプロジェクトルートを基点として、src ディレクトリ配下の全てのディレクトリは @/~でインポートできるという意味です。

"baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]
    },

Vitest に必要なライブラリをインストールして設定する

下記コマンドで設定に必要なライブラリをインストールします。

% npm install -D vitest happy-dom @vitest/coverage-v8 @testing-library/react @testing-library/user-event @testing-library/
jest-dom

今回はテストやブラウザ環境のエミュレートで使用でき、JSDOM よりも処理が早いとされている Happy DOM をインストールして使ってみます。

Vitest に必要な設定

他に Vitest を実行するための下記設定を tsconfig.json 記述していきます。

    "types": ["vitest/globals"]
  },
  "include": [
    "src",
    "node_modules/vitest/global.d.ts",
    "vite.config.ts",
    "vitest-setup.ts"
  ],

packgae.json の scripts を編集

npm run test とコマンドを実行すると vitest が実行されるように "test": "vitest" と追記します。

"scripts": {
	"dev": "vite",
	"build": "tsc && vite build",
	"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
	"preview": "vite preview",
	"test": "vitest"
},

vite.config.ts の設定

vite でプロジェクトを作成しているため、vite.config.ts ファイルも編集する必要があります。

vite.config.ts ファイルの編集を簡単にするため、vite-tsconfig-pathsをインストールして、vite.config.tsファイルの plugins に設定します。

設定することにより tsconfig.json の変更だけで、vite.config.ts ファイルの設定内容も自動的に反映することができます。

% npm install -D vite-tsconfig-paths

続けて、Vitest に必要な下記設定を記載します。

/// <reference types="vitest"/>
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import tsconfigPaths from "vite-tsconfig-paths";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), tsconfigPaths()],
  test: {
    globals: true,
    environment: "happy-dom",
    setupFiles: ["./vitest-setup.ts"],
  },
});

上記、setupFiles に記載した vitest-setup.ts はプロジェクト配下に作成します。

% touch vitest-setup.ts

続けて、下記設定を記載します。

import "@testing-library/jest-dom/vitest";

ここまでの設定内容

下記は、設定の参考情報(一例)としてご参照ください。

package.json

{
  "name": "vitest-setup-test",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview",
    "test": "vitest"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@testing-library/jest-dom": "^6.4.2",
    "@testing-library/react": "^14.2.2",
    "@testing-library/user-event": "^14.5.2",
    "@types/react": "^18.2.66",
    "@types/react-dom": "^18.2.22",
    "@typescript-eslint/eslint-plugin": "^7.2.0",
    "@typescript-eslint/parser": "^7.2.0",
    "@vitejs/plugin-react-swc": "^3.5.0",
    "@vitest/coverage-v8": "^1.4.0",
    "eslint": "^8.57.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.6",
    "happy-dom": "^14.3.9",
    "typescript": "^5.2.2",
    "vite": "^5.2.0",
    "vite-tsconfig-paths": "^4.3.2",
    "vitest": "^1.4.0"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]
    },
    "types": ["vitest/globals"]
  },
  "include": [
    "src",
    "node_modules/vitest/global.d.ts",
    "vite.config.ts",
    "vitest-setup.ts"
  ],
  "references": [{ "path": "./tsconfig.node.json" }]
}

vite.config.ts

/// <reference types="vitest"/>
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import tsconfigPaths from "vite-tsconfig-paths";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), tsconfigPaths()],
  test: {
    globals: true,
    environment: "happy-dom",
    setupFiles: ["./vitest-setup.ts"],
  },
});

vitest-setup.ts

import "@testing-library/jest-dom/vitest";

Vitest でテストコードを書いてみる

src/components/Input/TextInput.tsx

import React, { ChangeEvent, useState } from "react";

const TextInput = () => {
  const  = useState<string>("");

  return (
    <div>
      <input
        type="text"
        onChange={(e: ChangeEvent<HTMLInputElement>) => setText(e.target.value)}
        value={text}
        placeholder="Enter some text"
      />
      <p>{text}</p>
    </div>
  );
};

export default TextInput;

src/components/Input/TextInput.test.tsx

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import TextInput from "./TextInput";

test("TextInput Component test", () => {
  render(<TextInput />);

  const inputElement = screen.getByRole("textbox");
  expect(inputElement).toBeInTheDocument();
});

test("TextInput Event Test", async () => {
  const user = userEvent.setup();
  render(<TextInput />);

  const inputElement = screen.getByRole("textbox");
  await user.type(inputElement, "Vitest Test!");
  expect(screen.getByText("Vitest Test!")).toBeInTheDocument();
});

テスト実行

% npm run test

> vitest-setup-test@0.0.0 test
> vitest


 DEV  v1.4.0 /Users/anote.taro/dev/Introduction-to-React-Testing-with-Vitest/vitest-setup-test

 ✓ src/components/Input/TextInput.test.tsx (2)
   ✓ TextInput Component test
   ✓ TextInput Event Test

 Test Files  1 passed (1)
      Tests  2 passed (2)
   Start at  23:40:16
   Duration  535ms (transform 114ms, setup 112ms, collect 166ms, tests 47ms, environment 93ms, prepare 47ms)


 PASS  Waiting for file changes...
       press h to show help, press q to quit

まとめ

Vite + Vitest の導入からテストまでのハンズオンの過程を記載しました。

この記事が誰かのお役に立てば幸いです。

アノテーション株式会社について

アノテーション株式会社はクラスメソッドグループのオペレーション専門特化企業です。サポート・運用・開発保守・情シス・バックオフィスの専門チームが、最新 IT テクノロジー、高い技術力、蓄積されたノウハウをフル活用し、お客様の課題解決を行っています。当社は様々な職種でメンバーを募集しています。「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、アノテーション株式会社 採用サイトをぜひご覧ください。

参考資料