使用MockProvider模拟useMutation时触发InvariantError求助
问题背景
已成功使用Apollo Client的MockProvider模拟普通GQL查询,但模拟useMutation时触发InvariantError,无法正常执行测试。
组件代码(AddTaskForm.jsx)
import React, { useState } from 'react'; import { useMutation, gql } from '@apollo/client'; // Define the GraphQL mutation const ADD_TASK = gql` mutation AddTask($title: String!) { addTask(title: $title) { id title } } `; export const AddTaskForm = () => { const [title, setTitle] = useState(''); const [addTask, { data, loading, error }] = useMutation(ADD_TASK); const handleSubmit = async (e) => { e.preventDefault(); try { await addTask({ variables: { title } }); setTitle(''); } catch (err) { console.error('Error adding task', err); } }; return ( <div> <h2>Add New Task</h2> <form onSubmit={handleSubmit}> <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Enter task title" /> <button type="submit" disabled={loading}> {loading ? 'Adding...' : 'Add Task'} </button> </form> {error && <p>Error occurred: {error.message}</p>} {data && <p>Task {data.addTask} added successfully!</p>} </div> ); }; export default AddTaskForm;
测试代码(AddTaskForm.test.js)
// AddTaskForm.test.js import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import { MockedProvider } from '@apollo/client/testing'; import userEvent from '@testing-library/user-event'; import AddTaskForm, { ADD_TASK } from './AddTaskForm'; // Mock GraphQL mutation response const mocks = [ { request: { query: ADD_TASK, variables: { title: 'Test Task' }, }, result: { data: { addTask: { id: '1', title: 'Test Task', }, }, }, }, ]; test('should add a task successfully', async () => { render( <MockedProvider mocks={mocks} addTypename={false}> <AddTaskForm /> </MockedProvider> ); const input = screen.getByPlaceholderText(/enter task title/i); const submitButton = screen.getByText(/add task/i); userEvent.type(input, 'Test Task'); fireEvent.click(submitButton); expect(submitButton).toBeDisabled(); expect(submitButton).toHaveTextContent('Adding...'); await waitFor(() => { expect(screen.getByText('Task "Test Task" added successfully!')).toBeInTheDocument(); }); expect(input.value).toBe(''); });
错误信息
An error occurred! For more details, see the full error text at [链接已移除]
Invariant Violation: An error occurred! For more details, see the full error text at [链接已移除]
at new InvariantError (C:\dev\AKOM\frontend\node_modules\ts-invariant\lib\invariant.js:11:28)
at Object.originalInvariant [as invariant] (C:\dev\AKOM\frontend\node_modules\ts-invariant\lib\invariant.js:24:15)
排查关键点及解决方案
1. 组件渲染数据的错误
组件中成功提示直接渲染data.addTask(对象),会输出[object Object]而非期望的任务标题,导致测试无法匹配目标文本,进而触发内部错误。
修复组件:
将成功提示行修改为:
{data && <p>Task "{data.addTask.title}" added successfully!</p>}
2. 事件触发方式不规范
测试中用fireEvent.click(submitButton)触发提交,不如直接模拟表单提交更贴合真实用户行为,可能导致状态更新异常。
修改测试:
替换按钮点击为表单提交:
// 移除fireEvent.click(submitButton) const form = screen.getByRole('form'); userEvent.submit(form);
3. Mock匹配严格性验证
Apollo MockedProvider对mutation请求的匹配是严格的,需确保请求变量与mock变量完全一致(包括大小写、空格)。若测试输入与mock变量存在差异,会导致找不到匹配的mock触发错误。当前测试中变量一致,若后续有动态输入场景,可使用matchVariables选项实现灵活匹配。
4. Apollo版本对应错误排查
错误码76在Apollo Client 3.x中通常对应找不到匹配的mutation mock,需检查mock的result.data结构是否与mutation定义完全一致,确保字段无遗漏或错误。
验证修改
修复组件渲染和测试事件触发方式后,重新运行测试即可正常通过,不再触发InvariantError。
内容的提问来源于stack exchange,提问作者Oliver Watkins

