0
0

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

Posted at

はじめに

今回、発生したWarningはこちらです。

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となります。
こちらの解決方法となり至ってシンプルですが、個人的に勉強になったのでまとめたいと思います。

テストと実装

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()
    })
})
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

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()
    })
})

悲報
まだWarningが消えません

なぜWarningが消えない?

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

解決方法

waitForを使用する方法で、今回のWarningを消すことができました!

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0