Vitest で React Testing Library を使う

# はじめに
[Vitest](https://vitest.dev/) で [React Testing Library（RTL）](https://testing-library.com/docs/react-testing-library/intro/)を利用できるようにする手順のメモです。

# 前提
開発環境は以下の通りです。
- Windows11
- VSCode
- TypeScript 4.9.5
- React 18.2.0
- Vite 4.1.0
- Vitest 0.28.5
- @testing-library/react 14.0.0

また、事前に以下の準備をしています。
1. [`Vite`](https://vitejs.dev/) でプロジェクトファイルの作成（※手順は、[こちら](https://qiita.com/Yasushi-Mo/items/a85bb7a90ce96deae879)の記事にまとめてあります。）
1. [`Vitest`](https://vitest.dev/) のインストール
1. [`@testing-library/react`](https://testing-library.com/docs/react-testing-library/intro/) のインストール
1. テストディレクトリ（`src/__test__`）の作成
1. `App.tsx` のテストファイル（以下）の作成

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

describe("App", () => {
  test("renders App component", () => {
    render(<App />);

    screen.debug();
  });
});

```
この状態でテストを実行してもRTLの `render` を読み込むことができず、以下のようにテストが失敗してしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/207cfd35-b0dd-2c08-a571-eab43a2171b9.png)
そのため、これ以降の設定を行うことで、VitestでRTLを利用できるようにします（主に [Vitest with React Testing Library](https://www.robinwieruch.de/vitest-react-testing-library/) を参考にしながら設定を行います）。

# 1. jsdomインストール
RTLはReactコンポーネントをテストします。そのため、[jsdom](https://github.com/jsdom/jsdom)のようなライブラリを使い、Vitest上でHTMLを利用できるようにする必要があります。
以下のコマンドでjsdomをインストールします。
```
yarn add --dev jsdom
```
# 2. vite.config.ts 追加設定①
次に `vite.config.ts` に `test` の設定を追加します。
```vitest.config.ts
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
    open: true,
  },
  test: {
    environment: "jsdom",
  },
});
```
この時点では以下のように `test` の呼び出しエラーが発生します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/d0823238-83fc-3b6c-b91e-affde0737a3f.png)

[Configuring Vitest](https://vitest.dev/config/)には、vitestから `defineeConfig` をインポートする場合、設定ファイルの先頭に [triple slash command](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-) を利用してVitest型への参照を追加する必要があるとの記載があります。
ただ、この通りに設定したつもりでしたが、エラーが解消されなかったので、[Stack Overflow](https://stackoverflow.com/questions/72146352/vitest-defineconfig-test-does-not-exist-in-type-userconfigexport/74453402#74453402) に記載の方法で、エラーを回避しました。
```vitest.config.ts
import react from "@vitejs/plugin-react";
import type { UserConfig } from "vite";
import { defineConfig } from "vite";
import type { InlineConfig } from "vitest";

interface VitestConfigExport extends UserConfig {
  test: InlineConfig;
}

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
    open: true,
  },
  test: {
    environment: "jsdom",
  },
} as VitestConfigExport);
```

# 3. @testing-library/jest-dom インストール
RTLでjest-domを利用できるようにするため、[@testing-library/jest-dom](https://testing-library.com/docs/ecosystem-jest-dom/) を以下のコマンドでインストールします。
```
yarn add --dev @testing-library/jest-dom
```

# 4. テスト設定ファイルの追加
テストファイル用のディレクトリの下に `setup.ts` を追加します。
内容は以下の通りです。

:::note warn
追記：```@testing-library/jest-dom``` のバージョンによって記載内容が一部異なります。
:::

https://github.com/testing-library/jest-dom/releases

- ```@testing-library/jest-dom``` が v5以前の場合

```setup.ts
import matchers from "@testing-library/jest-dom/matchers";
import { cleanup } from "@testing-library/react";
import { afterEach, expect } from "vitest";

// extends Vitest's expect method with methods from react-testing-library
expect.extend(matchers);

// runs a cleanup after each test case (e.g. clearing jsdom)
afterEach(() => {
  cleanup();
});
```

- ```@testing-library/jest-dom``` が v6の場合

```setup.ts
import "@testing-library/jest-dom/vitest";
import { cleanup } from "@testing-library/react";
import { afterEach } from "vitest";

// runs a cleanup after each test case (e.g. clearing jsdom)
afterEach(() => {
  cleanup();
});

```

# 5. vite.config.ts 追加設定②
最後に先ほど作成したテスト設定ファイルを `vite.config.ts` で読み込みます。
加えて、VitestのAPIをグローバルに利用できるようにするため、`globals` を `true` にします。
```vite.config.ts
import react from "@vitejs/plugin-react";
import type { UserConfig } from "vite";
import { defineConfig } from "vite";
import type { InlineConfig } from "vitest";

interface VitestConfigExport extends UserConfig {
  test: InlineConfig;
}

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
    open: true,
  },
  test: {
    globals: true,
    environment: "jsdom",
    setupFiles: "./src/__tests__/setup.ts",
  },
} as VitestConfigExport);
```

:::note
`globals: true` を追加したことで、テストファイルから `import { describe, test } from "vitest";` を削除できます。
```App.test.tsx
import { render, screen } from "@testing-library/react";
import React from "react";
import { describe, test } from "vitest";
import App from "../../App";

describe("App", () => {
  test("renders App component", () => {
    render(<App />);

    screen.debug();
  });
});
```
:::

また、[Vitestのドキュメント](https://vitest.dev/config/#globals)には、`tsconfig.json` の `types` フィールドに `vitest/globals` を追加するよう記載があったので、追加します。

以上で設定完了です。
無事、テストが正常に動作しました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/ec99750d-e35d-de33-8587-7b87b64ae849.png)

# 参照
- [Configuring Vitest](https://vitest.dev/config/)
- [jsdom](https://github.com/jsdom/jsdom)
- [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
- [Testing Library](https://testing-library.com/)
- [Vite](https://vitejs.dev/) 
- [Vitest](https://vitest.dev/)
- [Vitest with React Testing Library](https://www.robinwieruch.de/vitest-react-testing-library/)
- [@testing-library/jest-dom](https://testing-library.com/docs/ecosystem-jest-dom/) 
- [@testing-library/jest-domをv5からv6にしたらテストが全て落ちたので調査する](https://zenn.dev/hbsnow/scraps/1127a21f30395e)