使用CRA、RTL和Jest在useEffect中模拟Promise遇阻求助
问题分析与解决思路
核心问题定位
你的测试代码存在几个关键错误,导致无法正确模拟Promise并验证异步渲染结果:
- 函数名笔误:测试中写成了
getBlocks.mockReturnValue,但实际导出的函数是getBooks - Promise模拟方式错误:
mockReturnValue传入了函数而非直接返回Promise实例,或未使用更简洁的mockResolvedValue - 同步查询无法捕获异步更新:
getByText是同步查询方法,会在组件异步更新前执行,导致找不到元素 - 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
相关产品推荐
相关产品推荐

