如何在useEffect执行完成后测试React组件的变更状态?
问题解决方法
出现你描述现象的核心原因:
- 你没有给
useState设置初始值,首次渲染时state自然是undefined,useEffect会在组件首次挂载完成后才执行,触发第二次渲染时state才更新为false,两次console输出是正常的React生命周期表现 - 你当前的测试代码存在两处核心错误:
- 自行mock了
React.useState,且mock的setValue是空函数,根本不会触发状态更新,破坏了原有组件的逻辑 - 测试用例中没有正确渲染
App组件,也没有等待生命周期执行完成就执行断言
- 自行mock了
修正方案
第一步:先修正组件的语法小问题
原组件中p标签闭合写法错误,同时如果需要验证state状态,建议将state渲染到DOM中(符合React Testing Library测用户可见行为的最佳实践):
const App = ({data}) => { const [state, setState] = useState(); // after useEffect runs state should be false console.log('test state', state) useEffect(() => { setState(!data) }, []) // 修正闭合标签,新增state渲染方便测试 return <p>data is {data}, state is {state}</p> }
第二步:修正测试用例
完全不需要自行mockuseState,直接使用React Testing Library的渲染和异步等待能力即可:
import { render, screen, waitFor } from '@testing-library/react'; import App from './App'; describe('App', () => { test('It should render the correct value and state updates after useEffect', async() => { // 先渲染组件 render(<App data={12} />); // 验证传入的props值12正常渲染 expect(screen.getByText(/data is 12/)).toBeInTheDocument(); // 等待useEffect执行完成,state更新为false await waitFor(() => { expect(screen.getByText(/state is false/)).toBeInTheDocument(); }) }) })
额外适配方案
如果不需要将state渲染到DOM,也可以用以下两种方式满足需求:
- 给
useState设置和useEffect执行结果一致的初始值,直接避免首次undefined输出:
const [state, setState] = useState(!data);
- 直接监听console输出,只判断第二次输出符合预期即可:
test('verify state after useEffect', () => { const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); render(<App data={12} />); // 取第二次的console输出验证 expect(consoleLogSpy).toHaveBeenNthCalledWith(2, 'test state', false); consoleLogSpy.mockRestore(); })
内容的提问来源于stack exchange,提问作者Asking
相关产品推荐
相关产品推荐

