如何用Vitest模拟useState钩子并验证setState是否被调用?
在Vitest中正确测试React组件调用setState的方法
我正把React应用的测试框架从Jest迁移到Vitest,需要测试某个组件在特定场景下是否调用setState函数。此前Jest的实现代码如下:
it('Component should call setState on render', () => { const setState = jest.fn(); jest.spyOn(React, 'useState').mockReturnValue(['', setState]); render(<Component />); expect(setState).toHaveBeenCalledWith('text'); });
但将jest.替换为vi.后测试失败,报错信息如下:
FAIL src/__tests__/components/Component.spec.tsx > Component > Component should call setState on render AssertionError: expected "spy" to be called with arguments: [ 'text' ]expected "spy" to be called with arguments: [ 'text' ] Received: Number of calls: 0 ❯ src/__tests__/components/Component.spec.tsx:82:19 80| ); 81| 82| expect(setState).toHaveBeenCalledWith('text'); | ^ 83| }); 84|
解决方法
直接替换jest为vi后测试失败,核心原因是Vitest对React内置Hook的模拟细节与Jest有差异,调整以下几点即可修复:
- 确保Mock时机与清理:在测试前完成useState的Mock,测试后恢复原始实现避免污染其他测试;
- 使用正确的Mock方式:优先用
vi.spyOn配合mockReturnValue或mockImplementation,确保模拟的useState返回预期的初始值和mock函数。
修正后的测试代码:
// 可选:在所有测试后自动恢复mock,避免残留影响 afterEach(() => { vi.restoreAllMocks(); }); it('Component should call setState on render', () => { // 创建mock的setState函数 const setState = vi.fn(); // 模拟React.useState,返回初始空字符串和mock函数 vi.spyOn(React, 'useState').mockReturnValue(['', setState]); // 渲染组件 render(<Component />); // 断言setState被正确调用 expect(setState).toHaveBeenCalledWith('text'); });
额外注意事项
- 确认你的组件确实会在挂载/渲染阶段调用
setState('text')(比如在useEffect中触发,而非直接在render函数内调用,否则会引发无限循环); - 如果使用React 18,确保测试环境已适配(比如@testing-library/react版本≥13),避免render行为差异导致的mock捕获失败;
- 若上述方法仍不生效,可以尝试用
mockImplementation替代mockReturnValue,效果一致但兼容性更好:vi.spyOn(React, 'useState').mockImplementation(() => ['', setState]);
内容的提问来源于stack exchange,提问作者Yuri Henrique
相关产品推荐
相关产品推荐

