React Testing Library如何断言异步函数执行的中间状态?
问题原因
测试失败是因为你mock的fetch是立即完成的(Promise直接resolve),React会对同步的状态更新做批量合并处理。点击按钮后,setIsLeaving(true)和紧接着的setIsLeaving(false)会被合并成一次状态更新,组件根本没机会渲染出显示'bye'的状态,所以测试里用getByText找不到对应的文本。
解决办法
你需要让mock的fetch稍微延迟完成,给React留出渲染'bye'状态的时间,具体有两种可行方式:
方式一:给mock的Promise加延迟
修改global.fetch的mock,把resolve逻辑放进setTimeout里,哪怕延迟0毫秒也能打破状态合并:
global.fetch = jest.fn(() => new Promise((resolve) => { setTimeout(() => { resolve({ json: () => Promise.resolve({ data: { id: 1, attributes: { titles: { en: 'Fullmetal Alchemist' }, synopsis: 'After losine.' } } }) }); }, 0); }) );
方式二:用findByText替代getByText
findByText本身结合了getByText和waitFor,可以自动等待目标元素出现。不过这种方式需要配合上面的延迟mock使用,修改测试断言部分:
await user.click(screen.getByRole('button', { name: /click me/i })); // 用findByText等待'bye'状态渲染完成 expect(await screen.findByText(/bye/i)).toBeInTheDocument(); await waitFor(() => { expect(screen.getByText(/hi/i)).toBeInTheDocument(); });
完整修改后的测试代码示例
import {render,screen,waitFor} from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { Test } from './Test'; import '@testing-library/jest-dom'; describe('Test component test', () => { global.fetch = jest.fn(() => new Promise((resolve) => { setTimeout(() => { resolve({ json: () => Promise.resolve({ data: { id: 1, attributes: { titles: { en: 'Fullmetal Alchemist' }, synopsis: 'After losine.' } } }) }); }, 0); }) ); it('should show "bye" text before showing "hi" text when data fetching is done', async () => { const user = userEvent.setup(); render(<Test />); expect(screen.getByText(/hi/i)).toBeInTheDocument(); await user.click(screen.getByRole('button', { name: /click me/i })); expect(await screen.findByText(/bye/i)).toBeInTheDocument(); await waitFor(() => { expect(screen.getByText(/hi/i)).toBeInTheDocument(); }); }); });
内容的提问来源于stack exchange,提问作者LY Hooi
相关产品推荐
相关产品推荐

