You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 07:52:56