3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

loading画面表示の設定が原因でReactテストがエラーになる

Posted at

初めに

現在学習記録アプリをReactで構築しています。
学習記録を登録するための登録ボタンを押したときに、学習記録リストの数が増えていることを確認するテストを行いました。
しかしテストを行ってもエラーが返ってきます。

  • 前提
    • 登録したデータはsupabaseに記録
    • supabaseから記録したデータを取得できるまでは、画面表示をloadingにしている

問題点

以下が問題のコードです。

if (loading) {
    return <div className="loading">ロード中...</div>; // ローディング中の表示
  }

  return (
    <>
    .
    .loadingが完了した後の画面表示の記述
    .
    </>

テストの記述

import { render, fireEvent, waitFor, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import { App } from "../App";
import React from "react";

test("adds a new todo item when the add button is clicked", async () => {
  const { getByPlaceholderText, getByText, findAllByRole } = render(
    <App />
  );

  // 操作前にリストアイテムの数を取得
  const initialItems = screen.getAllByTestId("study-item");
  const initialItemCount = initialItems.length;
  console.log(initialItemCount);

  // ユーザー操作をシミュレート(ToDo項目の追加)
  fireEvent.change(screen.getByTestId("content-input"), {
    target: { value: "新しいToDo" },
  });
  fireEvent.change(screen.getByTestId("time-input"), {
    target: { value: "1" },
  });
  fireEvent.click(screen.getByTestId("register-button"));

  await waitFor(() => {
    const updatedItems = screen.getAllByTestId("study-item");
    expect(updatedItems.length).toBe(initialItemCount + 1);
  });
});

ここで注目して欲しいのは

test("adds a new todo item when the add button is clicked", async () => {
  const { getByPlaceholderText, getByText, findAllByRole } = render(
    <App />
  );

この直下の記述です。
いきなりDOM要素を取得にいっているのですが、この時点ではloading画面が走っている状況であるため、まだ該当するDOM要素は取得できる状況ではありません。

解決策

ではどのように記述してエラーを解決したのかというと、loadingが完了してからDOM要素を取得するような記述にしました。

import { render, fireEvent, waitFor, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import { StudyMemo } from "../App";
import React from "react";

test("adds a new todo item when the add button is clicked", async () => {
  const { getByPlaceholderText, getByText, findAllByRole } = render(
    <StudyMemo />
  );

  await waitFor(() => {
    expect(screen.getByTestId("study-list")).toBeInTheDocument();
  });

  // 操作前にリストアイテムの数を取得
  // const initialItems = findAllByRole("listitem");
  // const initialItemCount = initialItems.length;
  const initialItems = screen.getAllByTestId("study-item");
  const initialItemCount = initialItems.length;
  console.log(initialItemCount);

  // ユーザー操作をシミュレート(ToDo項目の追加)
  fireEvent.change(screen.getByTestId("content-input"), {
    target: { value: "新しいToDo" },
  });
  fireEvent.change(screen.getByTestId("time-input"), {
    target: { value: "1" },
  });
  fireEvent.click(screen.getByTestId("register-button"));

  await waitFor(() => {
    const updatedItems = screen.getAllByTestId("study-item");
    expect(updatedItems.length).toBe(initialItemCount + 1);
  });
});

以下のコードを記述することによって、該当のDOM要素が取得できるまではそれ以降のテストが走らないような記述になります。

  await waitFor(() => {
    expect(screen.getByTestId("study-list")).toBeInTheDocument();
  });

chatGPTにawaitとwaitForの役割を聞いてみました。

await:処理が完了するまでそれ以降の記述が走らないようにする
waitFor:条件を満たすまで待つ

基本的にawaitとwaitForはセットで使うと思うので、役割を覚える必要はないかもしれませんが気になったので調べてみました。

終わりに

Reactの記述で状態管理をすることの難しさを実感しています...
難しいですがきっと重要なことなので根気よく理解する努力をしていきたいと思います!

3
2
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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?