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

测试含fetch请求的React自定义Hook:状态未更新问题解决

React自定义Hook测试问题解决

问题描述

我编写了一个简单的React自定义Hook,通过fetch调用API并将数据存入状态,返回该状态供组件使用。

useTodos.js代码:

import { useEffect, useState } from "react";

export function useTodos() {
  const [todos, setTodos] = useState([{ a: 1 }]);
  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/todos?_limit=1")
      .then((response) => response.json())
      .then((data) => setTodos(data));
  }, []);

  return todos;
}

useTodos.test.js代码:

import { waitFor, renderHook } from "@testing-library/react";
import { useTodos } from "./useTodos";

test("fetch should return data", async () => {
  const data = renderHook(() => useTodos());
  console.log(data.result.current);
  await waitFor(() => {
    console.log(data.result.current);
  });
});

实际输出:

console.log
      [ { a: 1 } ]   
      at Object.<anonymous> (src/useTodos.test.js:6:11)    
    console.log
      [ { a: 1 } ]    
      at src/useTodos.test.js:8:13

原本预期waitFor回调中的状态会是API返回的结果,但实际始终是useState设置的初始状态[{ a: 1 }],需要修正这个问题。


解决方法

1. 模拟全局fetch函数

测试场景下不应调用真实外部API,既影响测试速度也可能导致不稳定。我们需要模拟fetch的返回值:

// 在测试文件开头添加Mock逻辑
global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve([{ id: 1, title: "Test Todo", completed: false }]),
  })
);

2. 在waitFor中添加断言逻辑

waitFor通过断言失败来触发重试等待,单纯打印无法让它感知状态变化。必须添加断言来检测状态更新:

test("fetch should return data", async () => {
  const { result } = renderHook(() => useTodos());
  
  // 验证初始状态
  expect(result.current).toEqual([{ a: 1 }]);
  
  // 等待状态更新并验证API返回数据
  await waitFor(() => {
    expect(result.current).toEqual([{ id: 1, title: "Test Todo", completed: false }]);
  });
});

3. 可选:测试后清除Mock

避免Mock影响其他测试用例:

afterEach(() => {
  jest.restoreAllMocks();
});

完整测试代码示例

import { waitFor, renderHook } from "@testing-library/react";
import { useTodos } from "./useTodos";

// Mock全局fetch
global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve([{ id: 1, title: "Test Todo", completed: false }]),
  })
);

test("fetch should return data", async () => {
  const { result } = renderHook(() => useTodos());
  
  // 初始状态验证
  expect(result.current).toEqual([{ a: 1 }]);
  
  // 等待API请求完成,验证状态更新
  await waitFor(() => {
    expect(result.current).toEqual([{ id: 1, title: "Test Todo", completed: false }]);
  });
  
  // 额外验证fetch是否调用了正确的URL
  expect(fetch).toHaveBeenCalledWith("https://jsonplaceholder.typicode.com/todos?_limit=1");
});

// 测试后清除Mock
afterEach(() => {
  jest.restoreAllMocks();
});

原代码问题分析

  • 未Mockfetch:测试环境中fetch可能未被正确实现,导致真实API请求无法正常完成,状态始终未更新。
  • waitFor无断言:waitFor依赖断言失败来持续等待状态变化,单纯打印操作不会触发重试逻辑,因此直接输出初始状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:15:18