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
相关产品推荐
相关产品推荐

