Vitest を導入する



# 目次
[はじめに](#はじめに)
[Vitest をインストールする](#vitest-をインストールする)
[テストを書いてみる](#テストを書いてみる)
[Vitest の設定をする](#vitest-の設定をする)
[その他の設定](#その他の設定)
[import 文って省略できないの？](#import-文って省略できないの)
[test に必要なライブラリを devDependencies に追加する](#test-に必要なライブラリを-devdependencies-に追加する)
[App.tsx のテストを書いてみる](#apptsx-のテストを書いてみる)
[eslint-plugin-testing-library と eslint-plugin-jest-dom を追加する](#eslint-plugin-testing-library-と-eslint-plugin-jest-dom-を追加する)
[気になってた Warning が解決できた話](#気になってた-warning-が解決できた話)
[まとめ](#まとめ)
[次回のお知らせ](#次回のお知らせ)
[おまけ](#おまけ)


# はじめに
この記事は、TODO アプリを環境構築から作っていく様子を、少しずつ投稿していく、第４回目の記事になります。

前回までに、Vite プロジェクトを生成して、ESLint , Prettier の設定をしました。

今回は、TDD できる環境を整えていこうと思います。


<br>
👇 過去記事 

- 第１回： [TODOアプリをTDDしながら作ってみる](https://qiita.com/b-yuko/items/bf59604b53eda6c3a6f1)
- 第２回： [作成したViteプロジェクトのREADMEを読む](https://qiita.com/b-yuko/items/299e2c92f3f810186f5b)
- 第３回： [ESLintとPrettierの設定](https://qiita.com/b-yuko/items/b1dd18b5373f1b4629c7)

# Vitest をインストールする

### Vitest
Viteネイティブな次世代テストフレームワーク
実行速度が速い

https://vitest.dev/

Vitest の公式ドキュメントは日本語に翻訳されてないのかー・・・。
英語読めるようになりたい｡｡｡＿|￣|○


### インストール

:::note info
Vitest 1.0 には、Vite >= v5.0.0 と Node >= v18.00 が必要です。
:::

package.json を確認したら、Vite も Node も上記の要件はクリアしてる！

```shell
npm i -D vitest
```

https://vitest.dev/guide/


インストールコマンドを実行したら下記が表示された

```shell
added 54 packages, removed 7 packages, changed 19 packages, and audited 331 packages in 15s

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

1 moderate severity vulnerability

To address all issues, run:
  npm audit fix

Run `npm audit` for details.
```

なんで日本語じゃないのよ・・・。
必要そうなとこだけ翻訳すると、

- 深刻度が中程度の脆弱性が１件あるよ。
- すべての問題に対処するには `npm audit fix` を実行してね。
- 詳細が知りたかったら `npm audit` を実行してね。

ってことらしい。。。
そういうことなら、 `npm audit fix` を実行して解決してもらいましょう！

```shell
npm audit fix
```

```shell
changed 7 packages, and audited 331 packages in 7s

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

found 0 vulnerabilities
```
脆弱性は０件になったみたい！


ところで `npm audit fix` って何？

プロジェクトの脆弱性をスキャンして、脆弱な依存関係に互換性のあるアップデートを自動的にインストールしてくれるもの

らしい。ふーん。

https://docs.npmjs.com/cli/v9/commands/npm-audit#examples


とりあえずここでコミットしておきましょう！
git commit -m "Vitest をインストール & 脆弱性を解決した"
GitHubリポジトリ 👉 https://github.com/b-yuko/todo-app-tdd


# テストを書いてみる
公式ドキュメントにあるものをそのまま使ってテストできるかやってみる！
JS のところを TS で書いて・・・

```ts:sum.ts
export function sum(a: number, b: number) {
  return a + b
}
```

```ts:sum.test.ts
import { expect, test } from 'vitest'
import { sum } from './sum'

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})
```

package.json の scripts に test を追加して・・・

```diff:package.json
  {
    "scripts": {
+     "test": "vitest"
    },
  }
```

ターミナルから
```shell
npm run test
```

おぉ〜〜〜テスト出来てる！！
```shell
 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  15:50:19
   Duration  196ms (transform 0ms, setup 0ms, collect 10ms, tests 1ms, environment 0ms, prepare 73ms)
```

なんか満足😊


# Vitest の設定をする

Vitest の公式ドキュメントの Configuring Vitest を読んでみると、英語で読むのが苦痛になるが😓Viteを使っているなら vite.config.ts に Vitest 用の設定を追加すれば OK らしい。vitest.config.ts を作成する話も書いてあるけど、ドキュメントの WARNING の最後に `But we recommend to use the same file for both Vite and Vitest instead of creating two separate files.` とあるように、ViteとVitestの両方で同じファイルを使用することを勧められているので、今回は既存の vite.config.ts に設定を追加する方針で行きたいと思います。

https://vitest.dev/guide/#configuring-vitest

公式ドキュメントに書いてあるように追記してみました。

```diff:vite.config.ts
+ /// <reference types="vitest" />
  import { defineConfig } from 'vite'
  import react from '@vitejs/plugin-react-swc'

  // https://vitejs.dev/config/
  export default defineConfig({
    plugins: [react()],
+   test: {
+     // vitestの設定を書くところ
+   }
  })
```

# その他の設定
公式ドキュメントの Getting Started をサラッと読んで必要そうなところを書いてみる。

### Command Line Interface
上記で package.json の scripts に test を追加したけど
`"test": "vitest"` だと、コードが書き換わるのを監視しているっぽい。
必要なタイミングで `npm run test` を実行する場合は、`"test": "vitest run"` にするみたい。

へ〜。じゃあそうしよう！！

```diff:package.json
  {
    "scripts": {
-     "test": "vitest"
+     "test": "vitest run"
    },
  }
```

詳細はここを見ればよさそう👇

https://vitest.dev/guide/cli.html


他には特筆したいところがなかったな〜。。。


# import 文って省略できないの？
さっきお試しで書いた sum.test.ts に、expect と test をインポートする行があったんだよね〜。試しにコメントアウトしたら、expect と test が真っ赤になりました💦テストファイルだし、いちいちインポートしなくても済む方法ないのかな〜？

雑にググってみる。
import { expect, test } from 'vitest'　検索ポチっ

やば。ドンピシャな記事出てきた🎉

https://zenn.dev/ymiz/articles/d186c38e88067c

ありがたく拝読させてもらおう🙏

拝読させてもらった上で、こう変えた。

```diff:vite.config.ts
/// <reference types="vitest" />
  import { defineConfig } from 'vite'
  import react from '@vitejs/plugin-react-swc'

  // https://vitejs.dev/config/
  export default defineConfig({
    plugins: [react()],
    test: {
-     // vitestの設定を書くところ
+     globals: true
    }
  })
```

```diff:tsconfig.json
  {
    "compilerOptions": {
      "target": "ES2020",
      "useDefineForClassFields": true,
      "lib": ["ES2020", "DOM", "DOM.Iterable"],
      "module": "ESNext",
      "skipLibCheck": true,
+     "types": ["vitest/globals"],

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

      /* Linting */
      "strict": true,
      "noUnusedLocals": true,
      "noUnusedParameters": true,
      "noFallthroughCasesInSwitch": true
    },
    "include": ["src"],
    "references": [{ "path": "./tsconfig.node.json" }]
  }
```

おぉ〜〜〜。
インポート文をコメントアウトしても怒られなくなった！！

あ〜〜〜。ちゃんと公式ドキュメントに書いてあるんだ〜！！

https://vitest.dev/config/#globals

いや・・・でもこの書き方は私には読み取れる自信ないわ・・・。
日本語で記事書いてくれてる人に感謝しかない<(_ _*)>感謝

ではここまでで一旦コミットしておきましょう。
git commit -m "Vitest の設定を追加した"
GitHubリポジトリ 👉 https://github.com/b-yuko/todo-app-tdd

# test に必要なライブラリを devDependencies に追加する
### React Testing Library
Reactコンポーネントをテストするためのライブラリ

https://testing-library.com/docs/react-testing-library/intro/

```
npm install --save-dev @testing-library/react
```

[React Testing Library の Setup](https://testing-library.com/docs/react-testing-library/setup#using-without-jest) を読み進めていくと、Jest を使わず、Nodeでテストする場合は jsdom 入れてねって話が書いてあるので、それも入れましょう。
ブラウザ API をシミュレートするためのグローバル環境をセットアップするために使用できる global-jsdom というパッケージもあるみたいだけど、実は [DOM Testing Library の Setup](https://testing-library.com/docs/dom-testing-library/setup#using-without-jest) を読むと、Vitest を使用している場合は jsdom に環境を設定するだけでよいらしいので、jsdom のみインストールします。

```
npm install --save-dev jsdom
```

### jest-dom
Jest 用のカスタム DOM 要素マッチャを提供する Testing Library の付属ライブラリ
これを入れないと toBeInTheDocument などが使えないから入れます(Jest 使ってないのに?!)

https://testing-library.com/docs/ecosystem-jest-dom/

```
npm install --save-dev @testing-library/jest-dom
```

Testing Library の公式ドキュメントにはあまり詳しく書かれていないけど、ページの下の方に
> Check out [jest-dom's documentation](https://github.com/testing-library/jest-dom) for a full list of available matchers.

と書いてあり、jest-dom のドキュメントが存在することを教えてくれている。
ドキュメントを開いて読み進めていくと、`With Vitest` の文字が！！！
Jest 用と言いつつ、[Vitest を使う場合の設定方法](https://github.com/testing-library/jest-dom?tab=readme-ov-file#with-vitest)が書いてある。
その下には、TypeScript を使う場合の設定方法も。
さらにその下には、Jest 互換の expect を使う場合の設定方法も。
Vitest と TypeScript を使っているので、この３項目は設定が必要みたいです。

ざっと通して読み、
まずは、 frontend の直下に vitest-setup.ts を作り、import 文を書きます。

```ts:vitest-setup.ts
import '@testing-library/jest-dom/vitest'
```

次に、vite.config.ts に setupFiles を追加する。

```diff:vite.config.ts
  /// <reference types="vitest" />
  import { defineConfig } from 'vite'
  import react from '@vitejs/plugin-react-swc'

  // https://vitejs.dev/config/
  export default defineConfig({
    plugins: [react()],
    test: {
      globals: true,
+     setupFiles: ['./vitest-setup.ts']
    }
  })
```

次に、セットアップファイルを tsconfig.json に含めます。

```diff:tsconfig.json
  {
    "compilerOptions": {...},
-   "include": ["src"],
+   "include": [
+     "src",
+     "./vitest-setup.ts"
+   ],
    "references": [{ "path": "./tsconfig.node.json" }]
  }
```

最後に、vitest-setup.ts に Vitest の expect が使える設定を足します。

```diff:vitest-setup.ts
  import '@testing-library/jest-dom/vitest'
+ import * as matchers from '@testing-library/jest-dom/matchers'
+ import { expect } from 'vitest'

+ expect.extend(matchers)
```

ドキュメントからコピペするときに、拡張子が js だったり、ファイル名が jest-setup だったり、多少の読み替えが必要なので注意が必要だな〜と感じました。

ここまでで必要なライブラリと付随する設定も入れれたと思うのでコミットします！
git commit -m "テストに必要なライブラリと付随する設定をした"
GitHubリポジトリ 👉 https://github.com/b-yuko/todo-app-tdd

# App.tsx のテストを書いてみる
既存の App.tsx に対して、「Vite + React」って文字が表示されてるよね？ってテストを書いてみたいと思います。

frontend/src の直下に tests ディレクトリを作成して、その中に App.test.tsx を作成します。

```tsx:App.test.tsx
import { describe, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import App from '../App.tsx';

describe('App を render したとき', () => {
  test('「Vite + React」 という文字が表示される', () => {
    // Given

    // When
    render(<App />);

    // Then
    expect(screen.getByText('Vite + React')).toBeInTheDocument();
  });
});
```

これでなんとなくテストは書けたと思うので、テストを実行してみる。

ち〜〜〜ん。なんかエラーでた｡｡｡＿|￣|○

```
ReferenceError: document is not defined
```

ChatGPT 先生に、これ何？ってエラー文を雑に投げる。
DOM を扱うことができないからエラーになってるんじゃない？って感じのことを言われた・・・。

思い当たる節があるとすれば、jsdom の設定か何かが足りていないように思う。
Testing Library ではインポート文くらいしか書いてなかったしな〜・・・。

Vitest の公式ドキュメント内の検索で jsdom って入れたら何か見つかるかな？？
あ〜・・・。あった。

https://vitest.dev/config/#environment

テストファイルの一番上に

```diff:App.test.tsx
+ // @vitest-environment jsdom
  import ...
```

これを書けばよさそう。
上記を追加してテストを回すと、テストがパスすることを確認できた！

いや〜だけどさ、毎回テストファイルに書きたくないよね〜。
vitest の設定だし、vite.config.ts に書けるのかな？？

```diff:vite.config.ts
  /// <reference types="vitest" />
  import { defineConfig } from 'vite'
  import react from '@vitejs/plugin-react-swc'

// https://vitejs.dev/config/
  export default defineConfig({
    plugins: [react()],
    test: {
      globals: true,
      setupFiles: ['./vitest-setup.ts'],
+     environment: "jsdom"
    }
  })
```

適当に書いてみたら IntelliJ がサジェストしてくれて、いい感じに書けたっぽい。
`// @vitest-environment jsdom`を消して、テストを回してみるとパスすることが確認できた。
試しに App.tsx から「Vite + React」の文字がある部分をコメントアウトすると、テストが失敗するのでテストとしては大丈夫そう。
テストがパスしたら一度実装を壊してテストが失敗することを確認するようにって先輩エンジニアが言ってたときのメモを見つけた。メモしただけで終わってないよ！

そういえば、`import { describe, expect } from 'vitest';`って省略できるように設定したのに、IntelliJ が気を利かせて入れてくれたんだ・・・。いらないのに・・・。すでに設定したものについては import とかいらないんだけど、IntelliJ でここら辺の設定できるのかな？←知ってる人いたら教えて〜<(_ _*)>

あとは、さっき見てた [Vitest の公式ドキュメントの TIP](https://vitest.dev/config/#environment) に、
> Since Vitest 1.3.0 jsdom environment exposes jsdom global variable equal to the current JSDOM instance. If you want TypeScript to recognize it, you can add vitest/jsdom to your tsconfig.json when you use this environment:
> (訳) Vitest 1.3.0以降のjsdom環境では、現在のJSDOMインスタンスと同じグローバル変数jsdomが公開される。TypeScriptにこの環境を認識させたい場合は、tsconfig.jsonにvitest/jsdomを追加してください：

って書いてあったから、これだけ追加しておこうかな。

```diff:tsconfig.json
  {
    "compilerOptions": {
      "target": "ES2020",
      "useDefineForClassFields": true,
      "lib": ["ES2020", "DOM", "DOM.Iterable"],
      "module": "ESNext",
      "skipLibCheck": true,
-     "types": ["vitest/globals"],
+     "types": [
+       "vitest/globals",
+       "vitest/jsdom"
+     ],

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

      /* Linting */
      "strict": true,
      "noUnusedLocals": true,
      "noUnusedParameters": true,
      "noFallthroughCasesInSwitch": true
    },
    "include": [
      "src",
      "./vitest-setup.ts"
    ],
    "references": [{ "path": "./tsconfig.node.json" }]
  }
```

きりがいいのでコミットしておきます。
git commit -m "Appのテストを試しに書いて、必要な設定を追加した"
GitHubリポジトリ 👉 https://github.com/b-yuko/todo-app-tdd


# eslint-plugin-testing-library と eslint-plugin-jest-dom を追加する
Testing Library の公式ドキュメントにこの２つが載っていたので、入れておくか〜って感じです。
- [eslint-plugin-testing-library](https://testing-library.com/docs/ecosystem-eslint-plugin-testing-library)
- [eslint-plugin-jest-dom](https://testing-library.com/docs/ecosystem-eslint-plugin-jest-dom)

### eslint-plugin-testing-library を追加する
GitHub ページに詳細が書いてあるので、それを見ながら入れてみます。
- [eslint-plugin-testing-library on GitHub](https://github.com/testing-library/eslint-plugin-testing-library)

```
npm install --save-dev eslint-plugin-testing-library
```

```diff:.eslintrc.cjs
  module.exports = {
    ...,
    extends: [
      'eslint:recommended',
      'plugin:@typescript-eslint/recommended-type-checked',
      'plugin:react-hooks/recommended',
      'plugin:react/recommended',
      'plugin:react/jsx-runtime',
      'plugin:jsx-a11y/recommended',
+     'plugin:testing-library/react',
+     'plugin:testing-library/dom',
      'prettier'
    ],
    ...,
    plugins: [
        'react',
        'react-refresh',
        'jsx-a11y',
+       'testing-library'
    ],
    ...
  }
```

### eslint-plugin-jest-dom を追加する
先ほどと同様に GitHub を見ながら入れていきます。
- [eslint-plugin-jest-dom on GitHub](https://github.com/testing-library/eslint-plugin-jest-dom)

```
npm install --save-dev eslint-plugin-jest-dom
```

```diff:.eslintrc.cjs
  module.exports = {
    ...,
    extends: [
      'eslint:recommended',
      'plugin:@typescript-eslint/recommended-type-checked',
      'plugin:react-hooks/recommended',
      'plugin:react/recommended',
      'plugin:react/jsx-runtime',
      'plugin:jsx-a11y/recommended',
      'plugin:testing-library/react',
      'plugin:testing-library/dom',
+     'plugin:jest-dom/recommended',
      'prettier'
    ],
    ...,
    plugins: [
        'react',
        'react-refresh',
        'jsx-a11y',
        'testing-library',
+       'jest-dom'
    ],
    ...
  }
```

これでよさそう。

コミットします。
git commit -m "Testing Library の ESLint を導入した"
GitHubリポジトリ 👉 https://github.com/b-yuko/todo-app-tdd

# 気になってた Warning が解決できた話
ずっと前から気になってた Warning が２つほどあった。
ESLint の設定ファイルで、
module に対して `Unresolved variable or type module`
__dirname に対して `Unresolved variable or type __dirname`

積極的に解決しにいったわけではなくて、tsconfig.json の設定についてなんとなく色んなページをチラチラ見てたときに偶然見つけたの。
それがこのページ。

https://minerva.mamansoft.net/Notes/%F0%9F%93%9DVite%E3%81%A7tsconfig.paths%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%8C%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%AA%E3%82%89%E3%81%AA%E3%81%84

結論。
`@types/node` のインストールが必要なんだって。
Node.js の TypeScript 用 型定義ファイル らしい。

`@types/node` のバージョンは node のバージョンに対応しているらしいので、できるだけ近いバージョンを入れたほうがいいみたい。

https://dream-yt.github.io/post/use-at-types-node/

node のバージョンが、20.11.0 だったので、下記ページの Current Tags の中から 20.11 を探すと 20.11.25 があったのでこれを入れようと思います。

https://www.npmjs.com/package/@types/node?activeTab=versions

```
npm i -D @types/node@20.11.25
```

おぉ〜〜〜！
Warning が消えました！！！！！
だったら最初から Vite のパッケージに入れておいてくれればいいのにっ！！！！！

忘れる前にコミットしよう。
git commit -m "@types/node をインストールした"
GitHubリポジトリ 👉 https://github.com/b-yuko/todo-app-tdd


# まとめ
今回は、Vitest を入れて、テストで必要なライブラリを入れて、各種設定を行いました。
ひとまずこれで TDD ができる準備が整いました！

あ〜〜〜。長かった〜〜〜。
色んなところでつまずいて、たくさん時間を溶かしたけど、各種設定も公式ドキュメントに沿って書けたので納得感があってよきです。

これでようやく TDD しながら todo アプリが作れそうです！

# 次回のお知らせ
最初は、テキスト入力欄に文字を入れて、ボタンを押したら、バックエンドに情報が渡り、データベースに保存して、データベースに保存されたものを取り出して、フロントエンドに返して、画面に表示する。という一番シンプルなところをやってみたいと思います。
と言ってもまだバックエンドの準備してないし、結構ボリューミーなんです・・・。
細切れになるとは思うけど、ちょっとずつ進めていきます。

# おまけ
久しぶりに ESLint の公式ドキュメントを眺めたんだけど、今使ってる設定ファイルはもう既に非推奨になってました｡｡｡＿|￣|○
フロントエンド界隈は変わるのが早いってよく聞くけど、あぁ・・・こういうことなのか・・・。というのを感じた瞬間でした。
一瞬 ESLint 消して、入れ直そうかとも思ったけど、自己学習用だし、フォーカスしたいのそこじゃないし、今回は見て見ぬふりをしようと思います。
<br>
<br>
<br>


[目次に戻る](#目次)