Consider using the "jsdom" test environmentとexpect(...).toBeInTheDocument is not a functionの解決方法について

## はじめに
今回はViteとTypeScriptのアプリを作成し、テストを行った際にエラーが発生したので、解決方法をまとめます。

## 開発環境
| 言語・フレームワーク   | バージョン |
| ------------------  | ---------- |
| supabase/supabase-js| 2.45.4     |
| Node.js             | 18.17.1    |
| react               | 18.3.1     |
| typescript          | 5.5.3      |
| ts-jest             | 29.2.5     |
| vite                | 5.4.8      |

## テスト内容
テストの内容は、`h1`タグの`data-testid="title"`を取得するという簡単なものです。

【テスト対象ファイル】
```react:App.tsx
return (
    <>
      <h1 data-testid="title">TODOリスト</h1>
        ...
    </>
```

【テスト実行ファイル】
```react:<font color='lightsalmon'>【変更前】</font> App.spec.tsx
import { render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import App from '../App';

// createClient関数をモック
jest.mock('@supabase/supabase-js', () => ({
  createClient: jest.fn(() => ({
    from: jest.fn().mockReturnThis(),
    select: jest.fn().mockResolvedValue({ data: [], error: null }),
    insert: jest.fn().mockResolvedValue({ error: null }),
    delete: jest.fn().mockResolvedValue({ data: [], error: null }),
    eq: jest.fn().mockReturnThis(),
  })),
}));

describe('App', () => {
  test('タイトルがあること', async () => {
    render(<App />);
    const title = screen.getByTestId('title');

    await waitFor(() => {
      expect(title).toBeInTheDocument();
    });
  });
});
```



## エラー内容
エラーは１つ解消しては、また別のエラーが発生するというように複数生じたため、できるだけ時系列にまとめて書きたいと思います。

### 1. jsdomの環境変数の使い方が間違っている。
```zsh:console
FAIL  src/__tests__/App.spec.tsx
  App
    ✕ タイトルがあること (4 ms)

  ● App › タイトルがあること

    The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/configuration#testenvironment-string.
    Consider using the "jsdom" test environment.
```
対応策としては、テスト実行ファイルの先頭に以下のコメントを追記しました。
```react:App.spec.tsx
/**
 * @jest-environment jsdom
 */
```

### 2. toBeInTheDocument()が見つからない
1の対応策を行ったところ、続いて以下のエラーが発生しました。

```zsh:console
FAIL  src/__tests__/App.spec.tsx
  App
    ✕ タイトルがあること (1101 ms)

  ● App › タイトルがあること

    TypeError: expect(...).toBeInTheDocument is not a function

    Ignored nodes: comments, script, style
```

こちらについては、新たにインポート文をテスト実行ファイルに追加しました。

```react:App.spec.tsx
import '@testing-library/jest-dom';
```

### 3. dotenvが見つからない
最後のエラーは、dotenvが見つからないとのこと。
```zsh:console
 FAIL  src/__tests__/App.spec.tsx
  ● Test suite failed to run

    Cannot find module 'dotenv' from 'jest.setup.ts'

```
そのため、dotenvをインストールするコマンドを実行。
```zsh:terminal
npm i dotenv --save-dev

```

## テスト実行ファイル修正
「エラー内容」で記載した修正を行った後、テスト対象ファイルの`App.tsx`ではデータベースに登録している内容を非同期で取得して表示している箇所があります。
そちらの処理完了前にテスト実行ファイルの`App.spec.tsx`が実行されてしまうことを考慮し、`testid`を取得する処理も非同期に行うことで、無事テストが通りました。

【テスト実行ファイル】
```react:<font color='skyblue'>【変更後】</font> App.spec.tsx
import { render, screen, waitFor } from "@testing-library/react";
import App from "../App";
import '@testing-library/jest-dom';


describe("App", () => {
  test("タイトルがあること", async () => {
    render(<App />);
    await waitFor(() => {
      const title = screen.getByTestId("title");
      expect(title).toBeInTheDocument();
    })
  });
});
```

```zsh:console
PASS  src/__tests__/App.spec.tsx (7.081 s)
  App
    ✓ タイトルがあること (119 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.31 s
Ran all test suites.
```

## 最後に
テストを実行する際に多くの設定ファイルを編集する必要があり、依存関係の理解が少し進みました。
解釈が違うところや、別の書き方を推奨している場合などあれば、コメントで教えてください。

## 参考サイト

https://jestjs.io/docs/configuration#testenvironment-string

https://zenn.dev/naonao70/articles/26fa670a2ef31c

https://stackoverflow.com/questions/56547215/react-testing-library-why-is-tobeinthedocument-not-a-function

https://qiita.com/Kei-dev-1213/items/98799ae17c687f5c44f3