reactjsgraphqlreact-testing-libraryapollo-client

Cannot mock useMutation using MockProvider


I have been using MockProvider from Apollo Client successfully in mocking normal GQL queries, but when I try to mock mutations it does not seem to work.

If I try to mock a useMutation using the MockProvider I get an InvariantError.

The component I am trying to test is :

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;

My test file looks like this :

// 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'; // Mock provider from Apollo
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 component with MockedProvider to mock Apollo's useMutation
  render(
    <MockedProvider mocks={mocks} addTypename={false}>
      <AddTaskForm />
    </MockedProvider>
  );

  // Ensure the form and button are present
  const input = screen.getByPlaceholderText(/enter task title/i);
  const submitButton = screen.getByText(/add task/i);

  // Simulate typing into the input field
  userEvent.type(input, 'Test Task');

  // Simulate clicking the submit button
  fireEvent.click(submitButton);

  // Ensure the loading state is displayed
  expect(submitButton).toBeDisabled();
  expect(submitButton).toHaveTextContent('Adding...');

  // Wait for mutation to complete
  await waitFor(() => {
    expect(screen.getByText('Task "Test Task" added successfully!')).toBeInTheDocument();
  });

  // Check that input is cleared after task is added
  expect(input.value).toBe('');
});

However when I run the test I get an "Invariant" error that looks like this :

An error occurred! For more details, see the full error text at https://go.apollo.dev/c/err#%7B%22version%22%3A%223.11.1%22%2C%22message%22%3A76%2C%22args%22%3A%5B%5D%7D Invariant Violation: An error occurred! For more details, see the full error text at https://go.apollo.dev/c/err#%7B%22version%22%3A%223.11.1%22%2C%22message%22%3A76%2C%22args%22%3A%5B%5D%7D 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)

Navigating to this URL gives me nothing useful.

I have carefully analyzed my component and test and I can't see anything wrong with the code that I have.


Solution

  • You are not exporting ADD_TASK from your component file, so you pass a nonexistent variable (=undefined) into the mock.