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

useMutation加update后MockedProvider模拟结果异常

问题背景

现有表单提交时触发的注册业务逻辑,实现代码如下:

const [register, { loading }] = useMutation(RegisterDocument);

const router = useRouter();

const onSubmit = async (values: FormValues) => {
  const v = { ...values };
  delete v.confirmPassword;
  const res = await register({
    variables: { options: v },
    update: (cache, { data }) => {
      cache.writeQuery<MeQuery>({
        query: MeDocument,
        data: {
          __typename: 'Query',
          me: data?.register.user,
        },
      });
    },
  });
  if (res.data?.register.user) {
    router.push('/');
  }
};

对应的单元测试用例编写如下:

test('it should submit form without error', async () => {
  const firstName = faker.name.firstName();
  const surname = faker.name.lastName();
  const username = faker.internet.userName().replace('@', '');
  const email = faker.internet.email();
  const password = faker.internet.password(6, false, /^[a-zA-Z0-9_.-]*$/);

  const cache = new InMemoryCache().restore({});

  const variables = {
    options: { email, firstName, password, surname, username },
  };

  const user = { email, firstName, surname, username, id: 1, activated: false, photo: null };

  const mocks = [
    {
      request: { query: RegisterDocument, variables },
      result: { data: { register: { errors: null, user } } },
    },
  ];

  const { queryByTestId, container } = renderWithTheme(
    <MockedProvider mocks={mocks} cache={cache}>
      <Register />
    </MockedProvider>,
  );

  await updateRegisterInputs(container); // 填充表单输入值的工具函数

  await submitForm({ queryByTestId, testId: 'register-submit', loadingTestId: 'register-loading' }); // 触发表单提交的工具函数

  await waitFor(() => expect(onPush).toBeCalledWith('/'));
});

运行测试时出现固定异常现象:

  • 当register mutation配置中传入update属性时,返回结果res为{ data: { register: {} } },register字段是空对象
  • 移除mutation配置中的update属性后,res可以正常返回预期结果{ data: { register: { errors: null, user: [Object] } } }
  • 哪怕给update传入空函数update: () => {},依然会导致mutation模拟响应异常

问题原因

这个是@apollo/client测试组件MockedProvider的机制导致的,核心原因有两点:

  1. Mock返回数据缺少必填的__typename字段
    MockedProvider默认开启缓存规范化逻辑,所有写入缓存的对象必须携带__typename字段才能被正确识别和解析。你在mock中返回的register对象、内部嵌套的user对象都没有显式声明__typename,不符合缓存写入的格式要求。
  2. update参数会触发完整缓存写入流程
    如果不传update参数,Apollo不会对mock响应做缓存处理,会直接把mock的原始结果返回给mutation的Promise;一旦配置了update参数(哪怕传入空函数),Apollo都会先尝试把响应数据写入缓存、执行update回调,再把经过缓存处理后的结果返回。此时因为mock数据缺少__typename,缓存解析失败,最终返回给调用方的data就会变成空对象。

解决方法

给所有mock返回的GraphQL类型对象补充和Schema定义一致的__typename字段即可,修改mock部分代码:

const mocks = [
  {
    request: { query: RegisterDocument, variables },
    result: { 
      data: { 
        __typename: 'Mutation', // Mutation根类型固定字段
        register: { 
          __typename: 'UserResponse', // 替换为你Schema中register返回值的实际类型名
          errors: null, 
          user: {
            ...user,
            __typename: 'User' // 替换为你Schema中User类型的实际类型名
          } 
        } 
      } 
    },
  },
];

如果不想手动给每个mock对象添加__typename,也可以在初始化缓存时开启自动补全类型名的配置:

const cache = new InMemoryCache({ addTypename: true }).restore({});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 07:30:43