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('/')); });
运行测试时出现固定异常现象:
- 当
registermutation配置中传入update属性时,返回结果res为{ data: { register: {} } },register字段是空对象 - 移除mutation配置中的
update属性后,res可以正常返回预期结果{ data: { register: { errors: null, user: [Object] } } } - 哪怕给
update传入空函数update: () => {},依然会导致mutation模拟响应异常
问题原因
这个是@apollo/client测试组件MockedProvider的机制导致的,核心原因有两点:
- Mock返回数据缺少必填的
__typename字段MockedProvider默认开启缓存规范化逻辑,所有写入缓存的对象必须携带__typename字段才能被正确识别和解析。你在mock中返回的register对象、内部嵌套的user对象都没有显式声明__typename,不符合缓存写入的格式要求。 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
相关产品推荐
相关产品推荐

