Тестирование GraphQL

GraphQL — это мощный инструмент для построения API, который позволяет клиентам точно запрашивать те данные, которые им нужны, и ничего лишнего. Важно тестировать такие API, чтобы убедиться, что они работают корректно, а также минимизировать вероятность появления ошибок при их изменении. Jest, популярный фреймворк для тестирования JavaScript, предоставляет отличные возможности для тестирования серверов, реализующих GraphQL.

Основы тестирования GraphQL с Jest

При тестировании GraphQL важно учитывать как тестирование запросов и мутаций, так и проверку обработки ошибок, производительности и интеграцию с базой данных или другими внешними сервисами. Jest предоставляет удобные инструменты для написания юнит-тестов, мокирования зависимостей и проверки поведения серверов.

Установка и настройка

Для начала необходимо установить все необходимые зависимости. Для тестирования GraphQL с Jest потребуется несколько библиотек:

  • jest — для тестирования.
  • graphql — для работы с GraphQL.
  • apollo-server или express-graphql — для создания серверов GraphQL.
  • @testing-library/jest-dom — для улучшенного тестирования DOM (если тестируется фронтенд).
  • jest-mock — для мокирования функций.

Пример установки зависимостей:

npm install jest graphql apollo-server

Настройка тестовой среды для сервера GraphQL может быть выполнена с использованием специальных утилит для тестирования, таких как apollo-server-testing, которая предоставляет метод createTestClient для тестирования GraphQL-запросов.

npm install @apollo/server-testing

Мокирование запросов и мутаций

Для юнит-тестирования GraphQL-сервера необходимо замокировать зависимости, которые он использует. Важно разделить тесты на два типа: тестирование непосредственно запросов и тестирование функций, которые взаимодействуют с базой данных или внешними сервисами.

Пример мокирования запросов

Для мокирования запросов можно использовать функцию jest.fn(), которая позволяет подменить реальную функцию на фиктивную. Это особенно полезно при тестировании мутаций и обработчиков запросов, которые обращаются к базе данных.

const mockData = { id: 1, name: 'Test User' };

jest.mock('../resolvers/userResolver', () => ({
  getUser: jest.fn(() => mockData)
}));

Тестирование запросов и мутаций

Для тестирования запросов и мутаций необходимо использовать методы для отправки запросов на сервер GraphQL. Это можно сделать с помощью graphql-request или встроенной функции из apollo-server-testing.

Пример теста для GraphQL-запроса:

import { createTestClient } from '@apollo/server-testing';
import { ApolloServer, gql } from 'apollo-server';
import { typeDefs, resolvers } from '../schema';

const GET_USER_QUERY = gql`
  query getUser($id: ID!) {
    user(id: $id) {
      id
      name
    }
  }
`;

describe('GraphQL Queries', () => {
  let server;
  let testClient;

  beforeAll(() => {
    server = new ApolloServer({ typeDefs, resolvers });
    testClient = createTestClient(server);
  });

  it('fetches user by ID', async () => {
    const { data } = await testClient.query({
      query: GET_USER_QUERY,
      variables: { id: 1 },
    });

    expect(data.user).toEqual({
      id: '1',
      name: 'Test User',
    });
  });
});

В этом примере используется метод createTestClient, который создаёт тестовый клиент для отправки запросов на сервер. Это позволяет изолировать тесты от реальной базы данных и мокировать только необходимые данные.

Тестирование мутаций

Мутации могут быть протестированы аналогичным образом. Важно убедиться, что мутация корректно изменяет состояние сервера и возвращает правильный ответ.

Пример теста для GraphQL-мутации:

const CREATE_USER_MUTATION = gql`
  mutation createUser($name: String!) {
    createUser(name: $name) {
      id
      name
    }
  }
`;

describe('GraphQL Mutations', () => {
  it('creates a new user', async () => {
    const { data } = await testClient.mutate({
      mutation: CREATE_USER_MUTATION,
      variables: { name: 'New User' },
    });

    expect(data.createUser).toHaveProperty('id');
    expect(data.createUser.name).toBe('New User');
  });
});

Здесь проверяется, что мутация создает нового пользователя и возвращает правильные данные, включая ID нового пользователя.

Тестирование ошибок и исключений

Тестирование обработки ошибок — неотъемлемая часть работы с GraphQL. Необходимо проверять, что сервер корректно обрабатывает некорректные запросы, возвращает нужные коды ошибок и сообщения.

Пример теста для ошибок:

const GET_INVALID_USER_QUERY = gql`
  query getUser($id: ID!) {
    user(id: $id) {
      id
      name
    }
  }
`;

describe('GraphQL Error Handling', () => {
  it('returns an error for invalid query', async () => {
    const { errors } = await testClient.query({
      query: GET_INVALID_USER_QUERY,
      variables: { id: 'invalid' },
    });

    expect(errors).toHaveLength(1);
    expect(errors[0].message).toBe('User not found');
  });
});

Здесь проверяется, что при запросе пользователя с неверным ID сервер корректно возвращает ошибку.

Интеграционные тесты

Интеграционные тесты позволяют проверить работу всего сервера GraphQL с реальными базами данных или внешними сервисами. При интеграционном тестировании важно проверять не только логику запросов и мутаций, но и корректность работы всех зависимостей.

Интеграционные тесты могут выглядеть как обычные запросы, но они направлены на проверку взаимодействия различных компонентов системы.

Пример интеграционного теста с базой данных:

import { connectToDatabase, disconnectFromDatabase } from '../db';

beforeAll(async () => {
  await connectToDatabase();
});

afterAll(async () => {
  await disconnectFromDatabase();
});

it('fetches user from the database', async () => {
  const { data } = await testClient.query({
    query: GET_USER_QUERY,
    variables: { id: 1 },
  });

  expect(data.user).toHaveProperty('id', '1');
});

В этом примере выполняется подключение к базе данных перед тестами и её отключение после них.

Мокирование внешних API

Если сервер GraphQL взаимодействует с внешними API, например, через REST-запросы, то такие вызовы также следует замокировать. Для этого можно использовать библиотеки вроде nock или jest.mock() для подмены HTTP-запросов.

Пример мокирования HTTP-запроса:

import nock from 'nock';

beforeAll(() => {
  nock('https://api.example.com')
    .get('/user/1')
    .reply(200, { id: 1, name: 'Test User' });
});

it('fetches external data for user', async () => {
  const { data } = await testClient.query({
    query: GET_USER_QUERY,
    variables: { id: 1 },
  });

  expect(data.user).toEqual({
    id: '1',
    name: 'Test User',
  });
});

Здесь используется библиотека nock для замокирования запросов к внешнему API и возвращения фиктивных данных.

Повторное использование тестов

Для удобства можно создавать вспомогательные функции, которые будут использоваться в разных тестах. Например, если несколько тестов выполняют одинаковые запросы, можно вынести их в отдельные переменные или функции для упрощения кода.

Пример использования вспомогательных функций:

const createUser = (name) => {
  return testClient.mutate({
    mutation: CREATE_USER_MUTATION,
    variables: { name },
  });
};

it('creates a user with a short name', async () => {
  const { data } = await createUser('Short');
  expect(data.createUser.name).toBe('Short');
});

Это помогает сделать код более читаемым и удобным для тестирования.

Заключение

Тестирование GraphQL API с использованием Jest позволяет значительно повысить качество кода и уверенность в его стабильности. Создание эффективных юнит- и интеграционных тестов помогает обнаружить ошибки на ранних этапах разработки, а также обеспечивает легкость в обслуживании и изменении системы в будущем.