Именование тестов

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

Основные принципы именования

Ясность и конкретность — имена тестов должны чётко отражать, что именно тестируется, и какой результат ожидается. Если тест не может быть прочитан и понят без подробных комментариев, это свидетельствует о проблемах в структуре самого теста. Вместо того, чтобы использовать общие фразы типа test('works correctly'), лучше указать точное поведение функции или компонента, которое проверяется.

Использование глаголов в настоящем времени — так как тесты обычно описывают действия, важно использовать глаголы в настоящем времени. Это поможет формулировать тесты как утверждения о текущем поведении кода. Пример:

test('должен возвращать правильный результат при передаче отрицательных чисел', () => {
  expect(add(-1, -2)).toBe(-3);
});

Синтаксис и структура тестов

Jest предоставляет несколько вариантов синтаксиса для описания тестов. Наиболее распространённые функции — это test и it. В отличие от test, который используется с ключевым словом, как в примере выше, it предоставляет более читабельную структуру, особенно при использовании в описаниях:

it('должен возвращать правильный результат при передаче отрицательных чисел', () => {
  expect(add(-1, -2)).toBe(-3);
});

Этот синтаксис не влияет на поведение теста, но может улучшить восприятие, особенно когда тесты описывают поведение функции с точки зрения пользователя.

Описание поведения, а не реализации

Имена тестов должны описывать поведение системы, а не её внутреннюю реализацию. Это позволяет тестам оставаться актуальными даже в случае изменений в коде, не влияющих на конечный результат. Например, тест, проверяющий правильность вычисления суммы двух чисел, не должен зависеть от того, как именно эта сумма рассчитывается.

Пример:

// Плохо
test('должен вызывать функцию calculateSum и возвращать результат', () => {
  // внутренности теста, проверяющие вызов конкретной функции
});

// Хорошо
test('должен правильно суммировать два числа', () => {
  expect(add(2, 3)).toBe(5);
});

В последнем примере описано именно поведение системы, и тест остаётся актуальным даже если в будущем изменится способ реализации функции add.

Группировка тестов с помощью describe

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

Пример:

describe('Функция add', () => {
  test('должна правильно суммировать положительные числа', () => {
    expect(add(2, 3)).toBe(5);
  });

  test('должна правильно суммировать отрицательные числа', () => {
    expect(add(-2, -3)).toBe(-5);
  });

  test('должна возвращать правильный результат при смешанных знаках', () => {
    expect(add(-2, 3)).toBe(1);
  });
});

Использование describe позволяет обеспечить лёгкую навигацию по тестам и упрощает их восприятие, особенно при большом количестве проверок.

Примеры именования

Простые примеры:

test('должен возвращать правильную сумму двух чисел', () => {
  expect(sum(1, 2)).toBe(3);
});

Тестирование асинхронных функций:

test('должен корректно обрабатывать асинхронные запросы', async () => {
  const data = await fetchData();
  expect(data).toBeDefined();
});

Тестирование компонентов:

describe('Компонент Button', () => {
  test('должен рендерить текст по умолчанию', () => {
    const { getByText } = render(<Button />);
    expect(getByText('Нажми меня')).toBeInTheDocument();
  });

  test('должен вызывать функцию при клике', () => {
    const handleClick = jest.fn();
    const { getByText } = render(<Button onCl ick={handleClick} />);
    fireEvent.click(getByText('Нажми меня'));
    expect(handleClick).toHaveBeenCalledTimes(1);
  });
});

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

test('должен выбрасывать ошибку, если аргумент не является числом', () => {
  expect(() => {
    add('a', 3);
  }).toThrow('Аргументы должны быть числами');
});

Техники для улучшения читаемости

  1. Использование шаблонов и фраз — создание единых шаблонов для именования тестов позволяет повысить их предсказуемость. Например, все тесты, проверяющие функцию, могут начинаться с одинакового слова, например: “должен правильно…”, “должен возвращать…”, “должен выбрасывать ошибку…”.

  2. Минимизация избыточности — избегание избыточных описаний может значительно упростить восприятие тестов. Например, вместо того чтобы писать “должен вернуть результат, который равен 5”, можно просто написать “должен вернуть 5”.

  3. Использование параметризированных тестов — для проверки одинакового поведения с разными входными данными полезно использовать параметризированные тесты. Jest поддерживает это через функцию each.

describe('Функция add', () => {
  test.each([
    [1, 2, 3],
    [-1, -2, -3],
    [2, -3, -1],
  ])('должна правильно суммировать %i и %i', (a, b, expected) => {
    expect(add(a, b)).toBe(expected);
  });
});

Стандарты именования в команде

В рамках крупной команды или проекта полезно установить чёткие стандарты для именования тестов. Это позволяет избежать путаницы и несоответствий в стиле имен. Стандарты могут включать:

  • Использование одинаковых конструкций для тестов одной категории (например, все тесты на исключения могут начинаться с “должен выбрасывать ошибку”).
  • Единообразие в использовании описательных слов и фраз.
  • Чёткие правила для использования блоков describe и вложенных тестов.

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