You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React TypeScript项目Jest测试无法找到模拟Fetch返回文本问题

React useFetch钩子测试失败问题排查与解决

我用Vite搭建了一个React TypeScript小型应用,已安装所有所需的React测试包。编写了一个通过自定义useFetch钩子调用JSONPlaceholder接口并渲染文章列表的组件,Jest测试已经解决了fetch未定义的问题,但运行测试时无法找到模拟Fetch返回的文本,且组件显示“Unable to fetch data”错误。

相关代码

useFetch钩子代码

import { useEffect, useState } from "react";
function useFetch<T>(url: string) {
  const [apiData, setApiData] = useState<T>();
  const [pending, setPending] = useState(false);
  const [serverError, setServerError] = useState(false);

  useEffect(() => {
    setPending(true);
    fetch(url, {
      method: "GET",
    })
      .then((res) => {
        if (!res.ok) {
          throw Error("Unable to fetch data");
        }
        return res.json();
      })
      .then((data: T) => {
        setApiData(data);
        setPending(false);
      })
      .catch((err) => {
        setPending(false);
        setServerError(err.message);
      });
  }, [url]);

  return { apiData, pending, serverError };
}

export default useFetch;

组件代码

import useFetch from "./useFetch";
import { IPosts } from "./IPosts";

export default function APIDemo() {
  const { apiData, pending, serverError } = useFetch<IPosts[]>(
    "https://jsonplaceholder.typicode.com/posts"
  );

  return (
    <div>
      <h1>POSTS</h1>
      {pending && <p>Loading...</p>}
      {serverError && <p>{serverError}</p>}
      <>
        {apiData?.map((post: IPosts) => (
          <div key={post.id}>
            <h1>{post.title}</h1>
            <p>{post.body}</p>
          </div>
        ))}
      </>
    </div>
  );
}

测试代码(原版本)

import { act, render, screen, waitFor } from "@testing-library/react";
import APIDemo from "./APIDemo";

describe("API Demo", () => {
  beforeEach(() => {
    global.fetch = jest.fn().mockImplementation(() =>
      Promise.resolve({
        status: 200,
        json: () =>
          Promise.resolve([
            { id: 1, title: "Something1", body: "body1" },
            { id: 2, title: "Something2", body: "body2" },
          ]),
      })
    );
  });

  it("Should render heading POSTS", async () => {
    render(<APIDemo />);
    await waitFor(() => {
      screen.getByText("POSTS");
    });
  });

  it("Should render mocked post", async () => {
    await act(async () => render(<APIDemo />));
    expect(screen.getByText("Something")).toBeInTheDocument();
  });
});

测试失败结果

FAIL  src/APIDemo.test.tsx
API Demo
√ Should render heading POSTS (44 ms)
× Should render mocked post (10 ms)

API Demo › Should render mocked post

TestingLibraryElementError: Unable to find an element with the text: Something. This 
could be because the text is broken up by multiple elements. In this case, you can 
 provide a function for your text matcher to make your matcher more flexible.

 Ignored nodes: comments, script, style
 <body>
   <div>
    <div>
      <h1>
        POSTS
      </h1>
       <p>
         Unable to fetch data
       </p>
      </div>
    </div>
  </body>

   56 |   it("Should render mocked post", async () => {
   57 |     await act(async () => render(<APIDemo />));
  > 58 |     expect(screen.getByText("Something")).toBeInTheDocument();
      |                   ^
   59 |   });
   60 | });
   61 |

   at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:37:19)
   at node_modules/@testing-library/dom/dist/query-helpers.js:76:38
   at node_modules/@testing-library/dom/dist/query-helpers.js:52:17
   at node_modules/@testing-library/dom/dist/query-helpers.js:95:19
   at Object.<anonymous> (src/APIDemo.test.tsx:58:19)

   Test Suites: 1 failed, 1 total
   Tests:       1 failed, 1 passed, 2 total
   Snapshots:   0 total
   Time:        3.505 s
   Ran all test suites.

问题原因

  1. 模拟的Response缺少ok属性:useFetch钩子中通过res.ok判断请求是否成功,而你模拟的fetch返回对象只设置了status:200,没有显式定义ok: true,导致res.ok默认是false,直接触发了错误抛出,组件因此显示“Unable to fetch data”。
  2. 查询文本不匹配:测试用例中查询的是"Something",但模拟数据里的标题是"Something1"和"Something2",不存在对应的文本。
  3. 不必要的act包裹:React Testing Library的render方法已经内部处理了act逻辑,额外包裹反而可能导致异步状态处理异常。

修正后的测试代码

import { render, screen, waitFor } from "@testing-library/react";
import APIDemo from "./APIDemo";

describe("API Demo", () => {
  beforeEach(() => {
    global.fetch = jest.fn().mockImplementation(() =>
      Promise.resolve({
        status: 200,
        ok: true, // 必须添加该属性,确保res.ok为true
        json: () =>
          Promise.resolve([
            { id: 1, title: "Something1", body: "body1" },
            { id: 2, title: "Something2", body: "body2" },
          ]),
      })
    );
  });

  it("Should render heading POSTS", async () => {
    render(<APIDemo />);
    await waitFor(() => {
      screen.getByText("POSTS");
    });
  });

  it("Should render mocked post", async () => {
    render(<APIDemo />);
    // 使用findByText异步查询,匹配模拟数据中的实际文本
    const postTitle = await screen.findByText("Something1");
    expect(postTitle).toBeInTheDocument();
  });
});

关键说明

  • 添加ok: true到模拟的Response对象,让useFetch的请求成功判断通过,正常返回数据。
  • 调整查询文本为模拟数据中存在的"Something1",确保能找到对应元素。
  • 去掉不必要的act包裹,改用findByText(内置异步等待逻辑),符合React Testing Library的最佳实践。

内容的提问来源于stack exchange,提问作者exlnt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 09:02:35