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

React测试Apollo useSubscription Hook遇阻,寻求解决方案

如何测试Apollo Client的useSubscription Hook?解决"No more mocked responses"错误

我明白你在测试Apollo的useSubscription时遇到的困扰——加载状态测试能过,但验证数据返回时就报错说没有更多模拟响应。结合你的代码和报错信息,我来帮你梳理问题原因和解决方案:

问题核心原因

@apollo/react-testing的MockedProvider默认对每个模拟请求只提供一次响应。而你的场景中,组件可能因为重渲染(比如内部组件定义导致的重复挂载、useEffect重复触发mutation),导致订阅被多次发起,但你的mock只配置了一次响应,第二次请求就会触发"No more mocked responses"错误。

解决方案分步走

1. 给订阅Mock添加multiple: true

这是最直接解决报错的办法,告诉MockedProvider这个订阅的mock可以被重复使用:

// 修改你的mocks数组中订阅的配置
{
  request: {
    query: GET_RUNNING_DATA_SUBSCRIPTION,
  },
  result: {
    data: { onLastPowerUpdate: { result1: 'string', result2: 'string' }, },
  },
  multiple: true, // 新增这个属性,允许重复响应
}

2. 优化内部组件避免不必要的重订阅

你的SubscriptionData是定义在Dashboard内部的函数组件,每次Dashboard渲染时都会重新创建这个组件,导致useSubscription被反复执行、订阅被重复发起。把它提取到外部或者用React.memo包裹:

// 移到Dashboard组件外部,或用React.memo包裹
const SubscriptionData = React.memo(() => {
  const { data, loading } = useSubscription(GET_RUNNING_DATA_SUBSCRIPTION);
  if (loading) {
    return <Heading>Data loading...</Heading>;
  }
  const metrics = [];
  if (data) {
    console.log('DATA NOW CALLED IN TEST!');
    // 这里可以添加数据处理逻辑
  }
  return metrics;
});

const Dashboard: React.FC<RouteComponentProps & Props> = props => {
  // ... 原组件逻辑不变
};

3. 稳定useEffect的依赖项减少重复触发

你的useEffect依赖了startGetRunningData和stopGetRunningData——这两个是useMutation返回的函数,每次组件渲染都会生成新的引用,导致useEffect反复执行,间接触发订阅重连。用useCallback包裹让函数引用稳定:

const Dashboard: React.FC<RouteComponentProps & Props> = props => {
  const userHasProduct = !!props.user.serialNumber;
  const [startGetRunningData] = useMutation(START_GET_RUNNING_DATA);
  const [stopGetRunningData] = useMutation(STOP_GET_RUNNING_DATA);

  // 包裹mutation调用函数,确保引用稳定
  const memoizedStart = useCallback(() => {
    startGetRunningData({ variables: { serialNumber: props.user.serialNumber } });
  }, [startGetRunningData, props.user.serialNumber]);

  const memoizedStop = useCallback(() => {
    stopGetRunningData();
  }, [stopGetRunningData]);

  useEffect(() => {
    memoizedStart();
    return () => {
      memoizedStop();
    };
  }, [memoizedStart, memoizedStop]);

  // ... 原组件逻辑不变
};

4. 完善测试中的异步等待

在测试中确保等待订阅数据更新完成,再做断言:

it('should render subscription data', async () => {
  const component = create(
    <Provider store={store}>
      <MockedProvider mocks={mocks} addTypename={false}>
        <Router history={fakeHistory}>
          <Dashboard />
        </Router>
      </MockedProvider>
    </Provider>,
  );
  await wait(0); // 等待异步更新完成
  
  // 这里可以添加断言,比如检查加载状态消失,或者数据是否渲染
  expect(component.root.queryByType(Heading)).toBeNull();
});

总结

先尝试给订阅mock加上multiple: true解决报错,再通过组件优化减少不必要的重渲染和订阅发起,最后完善测试中的异步等待逻辑,应该就能顺利测试useSubscription的数据返回状态了。

内容的提问来源于stack exchange,提问作者Harry Lincoln

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 13:17:53