Vitest テストで表示を検証する際に注意すること

# はじめに
今回、発生したWarningはこちらです。

```npm
Warning: An update to MemoScreen 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 */
```

Reactの状態更新や副作用がテスト中に適切に管理されていない可能性があるため、発生するWarningとなります。
こちらの解決方法となり至ってシンプルですが、個人的に勉強になったのでまとめたいと思います。

# テストと実装

```tsx:MemoScreen.test.tsx
import { describe, expect, test } from "vitest";
import { render, screen } from "@testing-library/react";
import { MemoScreen } from "../../screens/MemoScreen";
import { SpyUserRepository } from "../repository/SpyUserRepository";

describe('MemoScreenのテスト',() => {
    test('MemoScreenがレンダリングされた時、userRepositoryのgetAllMemoを呼んでいる', () => {
        const spyUserRepository = new SpyUserRepository()
        render(<MemoScreen userRepository = {spyUserRepository}/>)

        expect(spyUserRepository.getAllMemo_isCalled).toBeTruthy()
    })
})
```

```tsx:MemoSreen.tsx
import { DefaultUserRepository, UserRepository } from "../repository/UserRepository.ts";
import { useEffect, useState } from "react";
import { UserMemo } from "../type/TypeMemoRepository.ts";
type Props = {
    userRepository?:UserRepository
}
export const MemoScreen = (
    {
        userRepository = new DefaultUserRepository()
    }: Props) => {
    const [memo, setMemo] = useState<UserMemo[]>([])

    useEffect(() => {
        const getAllMemo = async() => {
            const res = await userRepository.getAllMemo()
            setMemo(res)
        }
        getAllMemo()
    },[])

    return (
        <>
            <h2>Memo Record</h2>
        </>
    )
}
```

# act とは？
Warningの内容を見ると`act`でのwrapを推奨されています。
`act`とは何なのでしょうか？

`act`：Reactの状態や副作用が安定するのを待ってからテストを行うためのユーティリティ。これにより、テスト中に発生する可能性のある非同期状態の変更が完了するのを待ってからアサーション（検証）を行うための関数です。

# 実際に`act`でwrap

```tsx:MemoScreen.test.tsx
import { describe, test, expect } from "vitest";
import { render, screen, act } from "@testing-library/react";
import { MemoScreen } from "../../screens/MemoScreen";
import { SpyUserRepository } from "../repository/SpyUserRepository";

describe('MemoScreenのテスト', () => {
    test('MemoScreenがレンダリングされた時、userRepositoryのgetAllMemoを呼んでいる', () => {
        act(() => {
            render(<MemoScreen userRepository = {spyUserRepository}/>)
        })

        expect(spyUserRepository.getAllMemo_isCalled).toBeTruthy()
    })
})
```

:::note warn
悲報
まだWarningが消えません
:::

# なぜWarningが消えない？
`act`はレンダリングに対する非同期処理完了を待っていて、今回の実装側では`useEffect`内で`async`関数を使ってデータ取得を行っているため、**レンダリング後**の非同期処理完了を待つ必要があり、Warningが消えなかったと思います。

# 解決方法
`waitFor`を使用する方法で、今回のWarningを消すことができました！

```tsx:MemoScreen.test.tsx
import { describe, test, expect } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import { MemoScreen } from "../../screens/MemoScreen";
import { SpyUserRepository } from "../repository/SpyUserRepository";

describe('MemoScreenのテスト', () => {
    test('MemoScreenがレンダリングされた時、userRepositoryのgetAllMemoを呼んでいる', async() => {
        const spyUserRepository = new SpyUserRepository()
        render(<MemoScreen userRepository = {spyUserRepository}/>)

        await waitFor(() => {
            expect(spyUserRepository.getAllMemo_isCalled).toBeTruthy()
        })
    })
})
```

# 最後に
`waitFor`が1つのテストファイルに複数あると、テストが重くなりパフォーマンス低下につながってしまうので、テストファイルを細かく切り出すという少しプリミティブなやり方をしていますが、最もフェティッシュな方法を見つけ出したいですね。