MSW+Testing Library+RTK Query测试API失败时Jest报错问题
解决MSW+Testing Library+RTK Query测试API失败场景时Jest抛出错误的问题
问题背景
使用MSW、Testing Library和RTK Query测试自定义Hook的API调用失败场景:调用checkAuth函数,期望API失败时触发onAuthFailureCallback回调。当前断言已成功,但Jest仍抛出API错误导致测试不通过,尝试过try/catch和expect.rejects均无效。
业务代码
const checkAuth = useCallback(async () => { const token = await checkToken(); if (!isAuthenticated && token) { await authenticateUser(token); } else { dispatch(clearAuth); onAuthFailureCallback?.(); } }, [ isAuthenticated, authenticateUser, checkToken, dispatch, onAuthFailureCallback, ]); const authenticateUser = useCallback( async (token: string) => { try { await startAuthenticate(token, false).unwrap(); onAuthSuccessCallback?.(); } catch (err) { onAuthFailureCallback?.(err); } }, [startAuthenticate, onAuthSuccessCallback, onAuthFailureCallback], );
测试代码
it('given the user is not authenticated and the authentication fail an error should be sent back', async () => { server.use( rest.get(`${API_BASE_URL}api/v2.1/session`, (req, res, ctx) => { ctx.set('Authorization', 'Bearer token'); return res( ctx.status(401), ctx.json({ message: 'Could not be identified', }), ); }), ); jest .spyOn(sessionStorageUtils, 'getUserToken') .mockResolvedValue('my token'); const appStore = createStore({ preloadedState: { session: { ...sessionStateMock, isAuthenticated: false }, }, }); const { result } = renderHook(useAuth, { initialProps: { onAuthFailureCallback: onAuthFailureCallbackMock, onAuthSuccessCallback: onAuthSuccessCallbackkMock, }, wrapper: props => <HookWrapper {...props} appStore={appStore} />, }); await act(async () => { await result.current.checkAuth(); }); expect.assertions(1); return expect(onAuthFailureCallbackMock).toHaveBeenCalledWith({ status: 401, data: { message: 'Could not be identified' }, }); }); });
错误现象
断言成功,但Jest抛出错误:
thrown: Object { "error": Object { "data": Object { "message": "Could not be identified", }, "status": 401, }, ... }
解决方案
1. 用waitFor替代直接act包裹异步操作
act无法完全处理异步Hook的状态更新和错误捕获,换成Testing Library的waitFor来等待断言完成,确保异步流程被正确处理:
// 替换原来的act和断言部分 await waitFor(async () => { await result.current.checkAuth(); expect(onAuthFailureCallbackMock).toHaveBeenCalledWith({ error: { status: 401, data: { message: 'Could not be identified' }, }, }); });
注意:匹配实际传入回调的错误结构(从错误信息看,错误是嵌套在error字段下的)。
2. 给HookWrapper添加错误边界
Hook内部的未捕获错误会被Jest检测到,添加错误边界组件可以捕获这些错误:
// 定义简单的错误边界组件 class ErrorBoundary extends React.Component { state = { hasError: false }; static getDerivedStateFromError() { return { hasError: true }; } componentDidCatch() {} render() { if (this.state.hasError) return null; return this.props.children; } } // 修改HookWrapper const HookWrapper = ({ children, appStore }) => { return ( <Provider store={appStore}> <ErrorBoundary>{children}</ErrorBoundary> </Provider> ); };
3. 临时禁用Jest的未处理rejection检测
如果上述方法无效,可以临时屏蔽全局的未处理rejection监听(不推荐长期使用,建议先定位根源):
beforeAll(() => { // 屏蔽console.error避免错误日志污染 jest.spyOn(console, 'error').mockImplementation(() => {}); // 忽略未处理的rejection process.on('unhandledRejection', () => {}); }); afterAll(() => { // 恢复原有mock和监听 jest.restoreAllMocks(); process.removeAllListeners('unhandledRejection'); });
4. 检查RTK Query的错误处理链
确保startAuthenticate返回的Promise的所有rejection都被正确捕获:
- 确认
authenticateUser中的try/catch确实覆盖了unwrap()的错误抛出 - 检查应用中是否有全局的RTK Query错误处理逻辑(如
fetchBaseQuery的自定义错误处理)导致错误被重新抛出
内容的提问来源于stack exchange,提问作者Jérémie Bardon
相关产品推荐
相关产品推荐

