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

Apollo MockedProvider立即返回Null的React单元测试排查

React单元测试快照返回null问题排查

问题背景

我正在排查一个失败的React单元测试,测试使用react-test-renderer的TestRenderer。组件通过useQuery钩子结合GraphQL查询REST接口(借助apollo-link-rest),用<MockedProvider>包裹组件后调用.toJSON(),执行expect(tree).toMatchSnapshot()时快照立即返回null。

测试代码

import React from 'react';
import TestRenderer from 'react-test-renderer';
import { MockedProvider } from '@apollo/client/testing';
import GuitarList from '..';

it(`should render correctly`, async () => {
    const component = TestRenderer.create(
      <MockedProvider
        mocks={[LIST_GUITARS_MOCK]}
        addTypename={false}
        defaultOptions={{
          watchQuery: { fetchPolicy: 'no-cache' },
          query: { fetchPolicy: 'no-cache' },
        }}
      >
        <GuitarList />
      </MockedProvider>
    );

    const updateWrapper = async (time = 20) => {
      await act(async () => {
        await new Promise(res => setTimeout(res, time));
      });
    };

    const tree = component.toJSON()
    expect(tree).toMatchSnapshot();
  });
});

GraphQL查询代码

export const LIST_GUITARS = gql`
  query listGuitars(
    $id: Int!
    $limit: Int!
    $offset: Int!
  ) {
    listGuitars(input: {
      id: $id,
      limit: $limit,
      offset: $offset
    })
      @rest (
        type: "listGuitars",
        endpoint: "listGuitars",
        path: "/guitars/available/ListGuitars",
        method: "POST")
      {
        limit
        count
        offset
        guitars {
          name
          id
          price
          brand
          model
        }
      }
  }
`;

Mock数据

export const LIST_GUITARS_MOCK = {
  request: {
    query: LIST_GUITARS,
    variables: {
      limit: 25,
      offset: 0
    }
  },
  result: {
    data: {
      listGuitars: {
        guitars: [
          {
            name: "Red Guitar",
            id: 1,
            price: 1199,
            brand: "Fender",
            model: "Fender Original 1990"
          },
          {
            name: "Metal Guitar",
            id: 518,
            price: 699,
            brand: "Harley Benton",
            model: "Lefty Baritone 6 String"
          }
        ],
        count: 2,
        limit: 25,
        offset: 0
      }
    }
  }
};

依赖版本

{
    "@apollo/client": "^3.6.9",
    "apollo-link-debounce": "^3.0.0",
    "apollo-link-rest": "^0.9.0-rc.1",
    "graphql": "14.4.2",
    "qs": "^6.10.3"
}

问题原因分析

1. Mock变量与Query定义不匹配

Query中声明了三个必填变量:$id: Int!、$limit: Int!、$offset: Int!,但Mock的request.variables中仅提供了limit和offset,缺少id。Apollo的MockedProvider会严格匹配请求的变量,变量不匹配时Mock不会生效,组件会处于加载/错误状态,最终返回null。

2. 测试代码未等待状态更新

测试中定义了updateWrapper函数但未调用,直接获取组件快照。即使Mock是同步的,也需要等待React完成状态更新(比如useQuery从loading状态切换为success),否则获取到的是初始未渲染完成的状态。

3. 依赖版本兼容性问题

  • apollo-link-rest@0.9.0-rc.1是候选版本,与@apollo/client@3.6.9可能存在兼容性问题;
  • graphql@14.4.2版本较老,Apollo Client 3.x官方建议搭配graphql@15.x或graphql@16.x版本,版本不兼容可能导致MockedProvider无法正确处理REST查询。

修复方案

1. 补全Mock变量

在Mock的request.variables中添加必填的id字段(值根据组件实际传参调整):

export const LIST_GUITARS_MOCK = {
  request: {
    query: LIST_GUITARS,
    variables: {
      id: 0, // 补全必填变量
      limit: 25,
      offset: 0
    }
  },
  result: {
    data: {
      listGuitars: {
        guitars: [
          {
            name: "Red Guitar",
            id: 1,
            price: 1199,
            brand: "Fender",
            model: "Fender Original 1990"
          },
          {
            name: "Metal Guitar",
            id: 518,
            price: 699,
            brand: "Harley Benton",
            model: "Lefty Baritone 6 String"
          }
        ],
        count: 2,
        limit: 25,
        offset: 0
      }
    }
  }
};

2. 修改测试代码,等待状态更新

调用TestRenderer.act等待组件状态更新后再生成快照:

import React from 'react';
import TestRenderer from 'react-test-renderer';
import { MockedProvider } from '@apollo/client/testing';
import GuitarList from '..';
import { LIST_GUITARS_MOCK } from './mocks';

it(`should render correctly`, async () => {
  const component = TestRenderer.create(
    <MockedProvider
      mocks={[LIST_GUITARS_MOCK]}
      addTypename={false}
      defaultOptions={{
        watchQuery: { fetchPolicy: 'no-cache' },
        query: { fetchPolicy: 'no-cache' },
      }}
    >
      <GuitarList />
    </MockedProvider>
  );

  // 等待Mock请求完成和组件状态更新
  await TestRenderer.act(async () => {
    await new Promise(resolve => setTimeout(resolve, 0));
  });

  const tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});

3. 升级依赖版本(推荐)

升级graphql到兼容版本,同时尝试使用稳定版的apollo-link-rest:

{
  "@apollo/client": "^3.6.9",
  "apollo-link-debounce": "^3.0.0",
  "apollo-link-rest": "^0.9.0",
  "graphql": "^15.8.0",
  "qs": "^6.10.3"
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 12:35:22