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

使用CRA、RTL和Jest在useEffect中模拟Promise遇阻求助

问题分析与解决思路

核心问题定位

你的测试代码存在几个关键错误,导致无法正确模拟Promise并验证异步渲染结果:

  1. 函数名笔误:测试中写成了getBlocks.mockReturnValue,但实际导出的函数是getBooks
  2. Promise模拟方式错误:mockReturnValue传入了函数而非直接返回Promise实例,或未使用更简洁的mockResolvedValue
  3. 同步查询无法捕获异步更新:getByText是同步查询方法,会在组件异步更新前执行,导致找不到元素
  4. App组件缺失异常状态:未定义error状态和setError的对应变量,无法测试异常路径

修正后的代码示例

1. 完善App组件(补上error状态)

import { useState, useEffect } from 'react';
import { getBooks } from './books';

export const App = () => {
  const [books, setBooks] = useState(undefined);
  const [error, setError] = useState(null); // 新增error状态

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await getBooks();
        setBooks(response);
      } catch (error) {
        setError(`There seems to be an issue. Error: ${error.message}`);
      }
    };
    fetchData();
  }, []);

  return (
    <div>
      {error && <p>{error}</p>}
      {books && (
        <ul>
          {books.map((book) => (
            <li key={book.id}>{book.name}</li>
          ))}
        </ul>
      )}
    </div>
  );
};

2. 修正测试代码(覆盖成功/异常场景)

import { render, screen } from '@testing-library/react';
import { App } from './App';
import { getBooks } from './books';

jest.mock('./books', () => ({
  getBooks: jest.fn(),
}));

describe('App', () => {
  // 每次测试前清空mock调用记录,避免测试间干扰
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('渲染成功返回的书籍列表', async () => {
    // 模拟Promise成功返回数据
    getBooks.mockResolvedValue([{ id: 1, name: 'mock book' }]);

    render(<App />);

    // 使用异步查询方法findByText,等待组件更新后获取元素
    const bookItem = await screen.findByText('mock book');
    expect(bookItem).toBeInTheDocument();
    expect(getBooks).toHaveBeenCalledTimes(1);
  });

  it('Promise失败时渲染错误信息', async () => {
    // 模拟Promise抛出异常
    const mockError = new Error('Failed to fetch');
    getBooks.mockRejectedValue(mockError);

    render(<App />);

    // 等待错误信息渲染完成
    const errorMsg = await screen.findByText(`There seems to be an issue. Error: ${mockError.message}`);
    expect(errorMsg).toBeInTheDocument();
    expect(getBooks).toHaveBeenCalledTimes(1);
  });
});

关键修正说明

  • 正确模拟Promise:使用mockResolvedValue/mockRejectedValue替代手动构造Promise,这两个方法是Jest专门为异步函数提供的语法糖,更简洁不易出错
  • 异步查询元素:RTL的findBy*系列方法是异步的,内部自动结合waitFor等待组件状态更新,适合测试异步渲染场景;也可以用await waitFor(() => expect(screen.getByText('xxx')).toBeInTheDocument())替代
  • 隔离测试环境:beforeEach中调用jest.clearAllMocks(),确保每个测试用例的mock状态独立,避免前序测试影响后续结果

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 14:15:35