[Vue×Jest]Vue 3 に Jest を導入してテスト環境を構築する

# Vue 3 に Jest を導入してテスト環境を構築する

## はじめに

最近、業務で Vue.js を使う機会が増えてきたため、Vue 3 関連の記事も書いていこうと思います。今回は、Vue 3 で Jest を導入し、テスト環境を構築する方法をまとめます。

Vue の公式ドキュメントでは Vitest を推奨していますが、Jest との比較のために Jest を導入しようとしたところ、思いのほかハマってしまいました。これから Vue でテストを始める方の参考になれば幸いです。

## 対象

- Vue でテストを導入したい方
- Jest を使った Vue 3 のテスト環境構築方法を知りたい方

## 環境

- Docker (image: `node:22-bullseye`)
- MacOS (M3)

## Jest の導入と設定

### 必要なライブラリをインストール

Jest を Vue 3 で使うために、以下のライブラリをインストールします。

```sh
npm install --save-dev jest ts-jest @vue/test-utils @vue/vue3-jest jest-environment-jsdom
```

| パッケージ                  | 役割                                    |
| ---------------------- | ------------------------------------- |
| @vue/test-utils        | Vue コンポーネントのテストを行うためのライブラリ            |
| jest                   | JavaScript のテストランナー（v29）              |
| ts-jest                | TypeScript を Jest で直接実行できるようにする       |
| @vue/vue3-jest         | Vue 3 の `.vue` ファイルを Jest で実行できるようにする |
| jest-environment-jsdom | Jest の実行環境を `jsdom`（ブラウザ風の環境）にする      |

## Jest の設定詳細

以下の `jest.config.js` を作成し、Jest の設定を行います。

```typescript
const config = {
  preset: "ts-jest", // TypeScript を Jest で実行するためのプリセット
  testEnvironment: "jsdom", // ブラウザ環境をエミュレートするために jsdom を使用
  moduleFileExtensions: ["vue", "js", "ts", "json"], // テスト対象のファイル拡張子
  transform: {
    "^.+\\.vue$": "@vue/vue3-jest", // .vue ファイルを変換するための設定
    "^.+\\.(js|ts)$": ["ts-jest", { isolatedModules: true }], // TypeScript/JavaScript を Jest で実行できるように変換
  },
  collectCoverage: false, // カバレッジ収集を無効化（必要に応じて true に変更）
  collectCoverageFrom: [
    "src/**/*.{js,ts,vue}",
    "!src/main.ts",
    "!src/router/**",
    "!node_modules/**",
  ],
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/src/$1", // エイリアス `@` を `src/` にマッピング
  },
  testEnvironmentOptions: {
    customExportConditions: ["node", "node-addons"], // Node.js 環境向けの設定
  },
};

export default config;
```

## Jest の動作を確認する

Jest は CommonJS で動作するため、Vue の .vue ファイルをテストする際には `① Vue → ② JavaScript (ESM) → ③ JavaScript (CommonJS)` の 3 段階の変換 が必要になります。

### ① Vue ファイルを JavaScript (ESM) に変換

Vue の .vue ファイルは、HTML のテンプレート `<template>`、スクリプト `<script>`、スタイル `<style>` を含んだ特殊な形式です。
そのままでは JavaScript として実行できないため、`@vue/vue3-jest` を使って .vue ファイルを JavaScript に変換します。

```typescript:変換前（Vue ファイル）
<template>
  <button @click="count++">{{ count }}</button>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
```

```typescript:変換後（JavaScript / ESM）
import { ref } from 'vue';
export default {
  setup() {
    const count = ref(0);
    return { count };
  }
};
```

### ② JavaScript (ESM) を CommonJS に変換

Jest は CommonJS 形式で動作するため、JavaScript (ESM) の `import / export` を require / module.exports に変換する必要があります。これを `ts-jest` が担当します。

```typescript:変換後（CommonJS）
const vue = require('vue');
module.exports = {
  setup() {
    const count = vue.ref(0);
    return { count };
  }
};
```

### ③ Jest が変換後のファイルを実行してテストを実行

ここまでの変換を経て、Jest は .vue ファイルを適切に解釈し、テストを実行できるようになります。


## よくあるエラーと解決策

### **1. `Cannot find module '../components/TestVueJest.vue'` エラー**

TypeScript を使っている場合、.vue ファイルを import しようとすると、
TypeScript も 「こんな拡張子のファイル知らないよ！」 となります。
その結果、次のようなエラーが出ます。

```sh
error TS2307: Cannot find module '../components/TestVueJest.vue' or its corresponding type declarations.
```

`jest.config.js` の `transform` 設定に`isolatedModules: true`を追加し「単独の JavaScript ファイルとして変換すれば OK！」 と解釈してもらいエラーを回避します。

```typescript
transform: {
  "^.+\\.vue$": "@vue/vue3-jest",
  "^.+\\.(js|ts)$": ["ts-jest", { isolatedModules: true }],
},
```

参考：[GitHub Issue #4081](https://github.com/kulshekhar/ts-jest/issues/4081)

### **2. `ReferenceError: Vue is not defined` エラー**

Vue 3 は ESM（ECMAScript Modules） で書かれていますが、Jest はデフォルトで CommonJS（CJS） 形式で動作します。そのため、Jest は Vue の import 文を適切に解釈できず、
「Vue って何？」 となってしまい、ReferenceError: Vue is not defined というエラーが発生することがあります。

`jest.config.js` に以下を追加して、Jest が `Vue` のエクスポートを解釈できるようにします。

```typescript
testEnvironmentOptions: {
  customExportConditions: ["node", "node-addons"],
},
```

参考：[Stack Overflow](https://stackoverflow.com/questions/72428323/jest-referenceerror-vue-is-not-defined)


## 終わりに

Vue の公式推奨は `Vitest` ですが、Jest を使いたい場合でもいくつk設定を行えば動作させることができそうです。
実際にテストを書いて行く中で、他にもエラーが出てくるかもしれないので、どうしても必要ということでなければ`Vitest`の方が良さそうという印象でした。