or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

caching.mdcore-client.mderror-handling.mdindex.mdlink-system.mdmasking.mdreact-hooks.mdtesting.mdutilities.md
tile.json

testing.mddocs/

Testing

Mock implementations for testing GraphQL operations and React components. Apollo Client provides comprehensive testing support through mock links, providers, and utilities that enable reliable testing of GraphQL applications.

Capabilities

MockLink

Mock transport link for testing GraphQL operations without network requests.

/**
 * Mock link for testing GraphQL operations
 * Provides predefined responses for specific queries, mutations, and subscriptions
 */
class MockLink extends ApolloLink {
  constructor(mockedResponses: MockedResponse[], addTypename?: boolean, options?: MockLinkOptions);
  
  /**
   * Add additional mocked response
   * @param mockedResponse - Response to add to mock queue
   */
  addMockedResponse(mockedResponse: MockedResponse): void;
}

interface MockedResponse<TData = Record<string, any>, TVariables = Record<string, any>> {
  /** GraphQL request to match */
  request: {
    query: DocumentNode;
    variables?: TVariables;
    operationName?: string;
  };
  /** Static response data */
  result?: {
    data?: TData;
    errors?: readonly GraphQLError[];
    extensions?: any;
  };
  /** Dynamic response function */
  resultFunction?: () => FetchResult<TData>;
  /** Error to throw */
  error?: Error;
  /** Response delay in milliseconds */
  delay?: number;
  /** How many times this response can be used */
  maxUsageCount?: number;
}

interface MockLinkOptions {
  /** Show warnings for unused mocks */
  showWarnings?: boolean;
  /** Callback when request has no matching mock */
  onError?: (error: Error, operation: Operation) => void;
}

Usage Example:

import { MockLink } from "@apollo/client/testing";

const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`;

const CREATE_USER = gql`
  mutation CreateUser($input: CreateUserInput!) {
    createUser(input: $input) {
      id
      name
      email
    }
  }
`;

const mocks = [
  {
    request: {
      query: GET_USER,
      variables: { id: '1' }
    },
    result: {
      data: {
        user: {
          id: '1',
          name: 'John Doe',
          email: 'john@example.com',
          __typename: 'User'
        }
      }
    }
  },
  {
    request: {
      query: CREATE_USER,
      variables: {
        input: { name: 'Jane Doe', email: 'jane@example.com' }
      }
    },
    result: {
      data: {
        createUser: {
          id: '2',
          name: 'Jane Doe',
          email: 'jane@example.com',
          __typename: 'User'
        }
      }
    },
    delay: 500 // Simulate network delay
  }
];

const mockLink = new MockLink(mocks, true);

MockedProvider

React provider component for testing Apollo Client components with mocked responses.

/**
 * React provider for testing components with mocked GraphQL responses
 * Wraps components with Apollo Client configured for testing
 */
interface MockedProviderProps<TSerializedCache = {}> {
  /** Mock responses for GraphQL operations */
  mocks?: MockedResponse[];
  /** Add __typename to all objects */
  addTypename?: boolean;
  /** Default options for operations */
  defaultOptions?: DefaultOptions;
  /** Cache instance (defaults to InMemoryCache) */
  cache?: ApolloCache<TSerializedCache>;
  /** Local state resolvers */
  resolvers?: Resolvers;
  /** Type definitions */
  typeDefs?: string | string[] | DocumentNode | DocumentNode[];
  /** Additional props passed to children */
  childProps?: object;
  /** Child components */
  children?: React.ReactNode;
  /** Custom link (replaces MockLink) */
  link?: ApolloLink;
  /** Show warnings for unused mocks */
  showWarnings?: boolean;
}

interface MockedProvider<TSerializedCache = {}> extends React.ComponentType<MockedProviderProps<TSerializedCache>> {}

Usage Example:

import { render, screen, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import UserProfile from './UserProfile';

const GET_USER_PROFILE = gql`
  query GetUserProfile($id: ID!) {
    user(id: $id) {
      id
      name
      email
      bio
    }
  }
`;

const mocks = [
  {
    request: {
      query: GET_USER_PROFILE,
      variables: { id: '1' }
    },
    result: {
      data: {
        user: {
          id: '1',
          name: 'John Doe',
          email: 'john@example.com',
          bio: 'Software developer',
          __typename: 'User'
        }
      }
    }
  }
];

test('renders user profile', async () => {
  render(
    <MockedProvider mocks={mocks} addTypename={false}>
      <UserProfile userId="1" />
    </MockedProvider>
  );

  // Initially shows loading
  expect(screen.getByText('Loading...')).toBeInTheDocument();

  // Wait for data to load
  await waitFor(() => {
    expect(screen.getByText('John Doe')).toBeInTheDocument();
    expect(screen.getByText('john@example.com')).toBeInTheDocument();
    expect(screen.getByText('Software developer')).toBeInTheDocument();
  });
});

test('handles mutation', async () => {
  const UPDATE_USER = gql`
    mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {
      updateUser(id: $id, input: $input) {
        id
        name
        email
      }
    }
  `;

  const mutationMocks = [
    ...mocks,
    {
      request: {
        query: UPDATE_USER,
        variables: {
          id: '1',
          input: { name: 'John Updated' }
        }
      },
      result: {
        data: {
          updateUser: {
            id: '1',
            name: 'John Updated',
            email: 'john@example.com',
            __typename: 'User'
          }
        }
      }
    }
  ];

  render(
    <MockedProvider mocks={mutationMocks}>
      <EditUserForm userId="1" />
    </MockedProvider>
  );

  // Test mutation interaction
  const saveButton = await screen.findByText('Save');
  fireEvent.click(saveButton);

  await waitFor(() => {
    expect(screen.getByText('User updated successfully')).toBeInTheDocument();
  });
});

Testing Utilities

Additional utilities for testing GraphQL operations and cache behavior.

/**
 * Add realistic network delay to mock responses
 * @param min - Minimum delay in milliseconds
 * @param max - Maximum delay in milliseconds
 * @returns Random delay between min and max
 */
function realisticDelay(min: number = 200, max: number = 800): number;

/**
 * Mock subscription link for testing real-time features
 */
class MockSubscriptionLink extends ApolloLink {
  constructor();
  
  /**
   * Simulate subscription data
   * @param result - Subscription result to emit
   */
  simulateResult(result: FetchResult): void;
  
  /**
   * Simulate subscription completion
   */
  simulateComplete(): void;
  
  /**
   * Simulate subscription error
   * @param error - Error to emit
   */
  simulateError(error: Error): void;
}

Usage Example:

import { MockSubscriptionLink, realisticDelay } from '@apollo/client/testing';

// Realistic delay testing
const mocksWithDelay = [
  {
    request: { query: GET_USERS },
    result: { data: { users: [] } },
    delay: realisticDelay(100, 500)
  }
];

// Subscription testing
const COMMENT_SUBSCRIPTION = gql`
  subscription CommentAdded($postId: ID!) {
    commentAdded(postId: $postId) {
      id
      content
      author { name }
    }
  }
`;

test('handles subscription updates', async () => {
  const subscriptionLink = new MockSubscriptionLink();
  
  const client = new ApolloClient({
    link: subscriptionLink,
    cache: new InMemoryCache()
  });

  render(
    <ApolloProvider client={client}>
      <CommentList postId="1" />
    </ApolloProvider>
  );

  // Simulate subscription data
  act(() => {
    subscriptionLink.simulateResult({
      data: {
        commentAdded: {
          id: 'comment-1',
          content: 'Great post!',
          author: { name: 'Alice', __typename: 'User' },
          __typename: 'Comment'
        }
      }
    });
  });

  await waitFor(() => {
    expect(screen.getByText('Great post!')).toBeInTheDocument();
    expect(screen.getByText('Alice')).toBeInTheDocument();
  });
});

Error Testing

Testing error scenarios and error handling behavior.

// Network error testing
const networkErrorMock = {
  request: { query: GET_USER, variables: { id: '1' } },
  error: new Error('Network error occurred')
};

// GraphQL error testing  
const graphqlErrorMock = {
  request: { query: GET_USER, variables: { id: '1' } },
  result: {
    errors: [
      {
        message: 'User not found',
        locations: [{ line: 2, column: 3 }],
        path: ['user']
      }
    ]
  }
};

// Custom error response
const customErrorMock = {
  request: { query: GET_USER, variables: { id: '1' } },
  resultFunction: () => {
    throw new Error('Custom error from result function');
  }
};

Cache Testing

Testing cache behavior and modifications.

test('cache updates correctly after mutation', async () => {
  const cache = new InMemoryCache();
  
  // Pre-populate cache
  cache.writeQuery({
    query: GET_USERS,
    data: {
      users: [
        { id: '1', name: 'John', __typename: 'User' }
      ]
    }
  });

  const mocks = [
    {
      request: {
        query: CREATE_USER,
        variables: { input: { name: 'Jane' } }
      },
      result: {
        data: {
          createUser: {
            id: '2',
            name: 'Jane',
            __typename: 'User'
          }
        }
      }
    }
  ];

  render(
    <MockedProvider mocks={mocks} cache={cache}>
      <CreateUserForm />
    </MockedProvider>
  );

  // Trigger mutation
  const createButton = screen.getByText('Create User');
  fireEvent.click(createButton);

  await waitFor(() => {
    // Verify cache was updated
    const cachedData = cache.readQuery({ query: GET_USERS });
    expect(cachedData.users).toHaveLength(2);
    expect(cachedData.users[1].name).toBe('Jane');
  });
});

Types

interface MockLinkOptions {
  showWarnings?: boolean;
  onError?: (error: Error, operation: Operation) => void;
}

type ResultFunction<TData> = () => FetchResult<TData> | Promise<FetchResult<TData>>;

interface MockedRequest<TVariables = Record<string, any>> {
  query: DocumentNode;
  variables?: TVariables;
  operationName?: string;
  context?: Record<string, any>;
}

interface MockSubscriptionLinkOptions {
  onSubscribe?: (operation: Operation, handler: (result: FetchResult) => void) => void;
}