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

Apollo MockedProvider下useLazyQuery的onCompleted未触发测试问题咨询

问题原因与解决方法

你遇到的onCompleted不触发的问题,在Apollo Client 3.3.x版本配合MockedProvider测试时非常常见,可按照以下步骤逐一排查修复:

1. 先修复测试代码的路由配置错误

你当前的路由配置存在两处明显问题,会导致组件无法拿到正确的productId,最终请求参数和mock不匹配,无法触发成功回调:

  • 你配置的MemoryRouter初始路径是contracts/311,但匹配组件的路由是products/:productId,路径完全不匹配,组件拿不到正确的商品ID参数
  • 内层已经使用MemoryRouter,外层render配置又加了wrapper: BrowserRouter,两层路由会导致路径解析冲突

修改后的render函数参考:

const renderComponent = (mocksData) => {
  render(
    <MockedProvider
      mocks={mocksData}
      defaultOptions={{
        query: { fetchPolicy: "no-cache" },
        watchQuery: { fetchPolicy: 'no-cache' },
      }}
      addTypename={false}
    >
      <ServicesContextProvider freightRateServices={{}}>
        <ContractInformationPageProvider>
          <MemoryRouter initialEntries={["/products/311"]}>
            <Route path="products/:productId">
              <ContractInformationPage />
            </Route>
          </MemoryRouter>
        </ContractInformationPageProvider>
      </ServicesContextProvider>
    </MockedProvider>
    // 删掉外层BrowserRouter wrapper
  );
};

2. 检查mock数据的匹配性

MockedProvider会严格匹配请求的query和变量,只要有一处不一致就不会返回成功结果,自然不会触发onCompleted:

  • 确保mock中使用的GET_DATA和组件内引入的是同一个导出对象,不要手动重写query结构
  • 确保mock的request.variables和组件实际请求的变量完全一致,包括数据类型,比如id如果是数字311,mock中就不能写字符串"311",first、page、isBase三个字段也要完全匹配
  • 可以在mock的result函数中加日志验证是否匹配成功,示例:
const testMock = [
  {
    request: {
      query: GET_DATA,
      variables: { id: 311, first: 999999, page: 1, isBase: true }
    },
    result: () => {
      console.log('mock匹配成功')
      return {
        data: {
          dataWithFilters: {
            data: {/* 你的测试返回数据 */}
          }
        }
      }
    }
  }
]

如果测试时没有打印上述日志,说明mock匹配失败,优先排查变量和query的一致性。

3. 测试时等待异步操作完成

MockedProvider的响应是异步的,不能直接用同步查询方法断言,必须等待请求返回:

  • 优先用findBy*系列异步查询方法,等待onCompleted执行后渲染的内容出现
  • 如果需要验证状态变化,用waitFor包裹断言逻辑,示例:
test('验证onCompleted触发后数据正常渲染', async () => {
  renderComponent(testMock)
  // 等待onCompleted执行后渲染的目标内容,比如某个数据字段
  const dataDom = await screen.findByText('预期渲染的测试数据')
  expect(dataDom).toBeInTheDocument()
})

4. 修复useLazyQuery的配置写法(针对3.3.16版本已知bug)

Apollo Client 3.3.16版本存在配置合并bug:如果在useLazyQuery定义时传了onCompleted,调用loadData时又传了其他配置,会覆盖掉定义时的回调,导致onCompleted丢失。建议二选一使用以下写法:

写法1:所有配置放在useLazyQuery定义中,调用时不传参数
const [loadData, { loading }] = useLazyQuery(
  GET_DATA,
  {
    variables: {
      id: productId,
      first: 999999,
      page: 1,
      isBase: true
    },
    onCompleted: handleData,
    fetchPolicy: "network-only"
  }
);

useEffect(() => {
  loadData(); // 不再重复传配置
}, [loadData, productId]); 
写法2:所有配置放在loadData调用时传入
const [loadData, { loading }] = useLazyQuery(GET_DATA);

useEffect(() => {
  loadData({
    variables: {
      id: productId,
      first: 999999,
      page: 1,
      isBase: true
    },
    onCompleted: handleData,
    fetchPolicy: "network-only"
  });
}, [loadData, productId]); 

内容的提问来源于stack exchange,提问作者Zichen Ma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 04:39:03