【React】Jestでactの警告が出た。An update to Id inside a test was not wrapped in act(...).

## はじめに
```beforeEach``` 内で ```render``` を実施すると 「```act``` で囲ってください」と警告が出るようになってしまいましたので、原因と解決策を調べました。

# 問題
実装したテストはこちらです。
画面遷移をテスト環境でも実施するために、```createMemoryHistory``` を使用して、pathを「/cards/test」に変えています。
また、DOMの```render``` を毎回のテスト前に行いたいため ```beforeEach``` 内に実装しています。

```App.spec.tsx
describe("名刺カードのテスト", () => {
  let history;
  beforeEach(() => {
      history = createMemoryHistory();
      history.push("/cards/test");
      render(
        <ChakraProvider value={defaultSystem}>
          <Router location={history.location} navigator={history}>
            <Routes>
              <Route path="/cards/test" element={<Id />} />
            </Routes>
          </Router>
        </ChakraProvider>
      );
  });
  test("名前、自己紹介、技術、GithubとQiitaとXのアイコンが表示されていること。", async () => {
    await waitFor(() => {
      expect(screen.getByText("Loading...")).toBeInTheDocument();
    });
    const github = screen.getByTestId("github_link");
    const qiita = screen.getByTestId("qiita_link");
    const x = screen.getByTestId("x_link");
    await waitFor(() => {
      expect(screen.getByText("テスト太郎")).toBeInTheDocument();
      expect(screen.getByText("Hello World!")).toBeInTheDocument();
      expect(
        screen.getByText(TestRegisteredJson.skillName)
      ).toBeInTheDocument();

      expect(github).toBeInTheDocument();
      expect(qiita).toBeInTheDocument();
      expect(x).toBeInTheDocument();
    });
  });
});
```

そして、 ```npm run test``` を実施すると
以下のような警告が赤字でターミナル上に出ます。

```
An update to Id inside a test was not wrapped in act(...).
    
    When testing, code that causes React state updates should be wrapped into act(...):
    
    act(() => {
      /* fire events that update state */
    });
    /* assert on the output */
    
    This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
```
これは「ステートの更新を含んでいる場合は、```act``` でレンダリング部分を囲ってください」と指摘されています。

え？```beforeEach``` は ```test``` の前に呼び出されるはずで、```test``` が始まる頃には ```beforeEach``` の処理は完了しているから大丈夫と思ったのですが
なぜかうまくいきません。。


# 原因
```beforeEach``` 内で呼び出されている ```render``` と再レンダリングのタイミングが原因だと考えています。

### 実装したテスト内では以下の要因で再レンダリングします。
- ```<Id/>``` 内で実装した ```state``` の更新による再レンダリング
- ```history.push("/cards/test");``` の際にReact Routerがルートの変更を検知し、```Id``` コンポーネントの再レンダリングをトリガーする
### 原因は再レンダリングに対応しきれていなかったからと推測
本当ならデフォルトで ```render``` 関数などは ```act``` が内部的に使用されているので、再レンダリングにも対応しているはずです。
※```act``` という関数はstateの更新が完了するまで待つことができる関数です。
※```act(() => {}``` で対象のコンポーネントを囲ってあげる必要があります。

ただ、もし```Router``` の再レンダリングと ```render``` が同じようなタイミングであれば、```beforeEach``` 内の処理が完了してしまい、```test``` 中に再レンダリングが走ります。
※```act```で囲っている時には、再レンダリングが起きない

### だからこうなる
なので、```beforeEach``` 内で ```render``` まで完了し、 ```test``` が開始されたとしても、コンポーネントは再レンダリング中でまだDOMが安定していない状態で ```test``` が実施されてしまい、```act``` の警告が出てしまうということ？？



# 解決方法
```beforeEach``` 内を ```await waitFor``` を使用して、再レンダリングが完了するまで待つようにします。

:::note info
await waitForを使用すると？
内部のコードが引き起こしたReactの更新（データフェッチ、初回レンダリング、ルーティングによるレンダリングなど）が完了し、DOMが安定するまで待機します。
:::

```tsx
  let history;
  beforeEach( async() => { // async追加
      // await waitForで囲う
      await waitFor(() => {
          history = createMemoryHistory();
          history.push("/cards/test"); // 再レンダリングがトリガ―
          render(
            <ChakraProvider value={defaultSystem}>
              <Router location={history.location} navigator={history}>
                <Routes>
                  <Route path="/cards/test" element={<Id />} />
                </Routes>
              </Router>
            </ChakraProvider>
         );
       });
    });
    ///...
```

警告が出なくなり、テストも成功するようになりました◎
```
PASS  src/__tests__/App.spec.tsx
  名刺カードのテスト
    ✓ 名前、自己紹介、技術、GithubとQiitaとXのアイコンが表示されていること。 (120 ms)
    
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.35 s
```

# 参考
actについて

https://github.com/threepointone/react-act-examples/blob/master/sync.md

https://ja.legacy.reactjs.org/docs/testing-recipes.html#act