Тестирование Node.js приложений

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

Основы Jest для Node.js

Jest представляет собой фреймворк для юнит-тестирования, который предлагает все необходимое для создания и выполнения тестов, а также имеет встроенные функции для мокирования, покрытия кода и асинхронных тестов. Он часто используется для тестирования как фронтенда, так и бэкенда, включая Node.js приложения.

Установка Jest в проект Node.js

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

npm install --save-dev jest

После установки, в файле package.json нужно добавить скрипт для запуска тестов:

{
  "scripts": {
    "test": "jest"
  }
}

Теперь тесты можно запускать с помощью команды:

npm test

Jest автоматически найдет все тестовые файлы в проекте, если они следуют общепринятому соглашению об именах, например *.test.js или *.spec.js.

Структура теста в Jest

Jest использует три основные функции для написания тестов: describe, it и expect.

  • describe — используется для группировки тестов, особенно если тестов много и они касаются одного и того же компонента.
  • it или test — описывает отдельный тест. Обычно это утверждения о том, что должно происходить с кодом.
  • expect — используется для проверки значений. С помощью этого метода можно делать утверждения о результатах выполнения кода.

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

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

  it('должна возвращать число при сложении с нулем', () => {
    const result = add(0, 3);
    expect(result).toBe(3);
  });
});

Асинхронное тестирование в Jest

Асинхронное тестирование — неотъемлемая часть разработки для Node.js приложений, так как многие операции (например, работа с базой данных или сетевые запросы) выполняются асинхронно.

Jest предоставляет несколько способов работы с асинхронными операциями:

  1. Возвращение промиса:
it('должен возвращать промис', () => {
  return someAsyncFunction().then(data => {
    expect(data).toBe('expected value');
  });
});
  1. Использование async/await:
it('должен ожидать результат асинхронной функции', async () => {
  const result = await someAsyncFunction();
  expect(result).toBe('expected value');
});
  1. Использование done для старого синтаксиса:
it('должен работать с обратным вызовом', done => {
  someAsyncFunction((data) => {
    expect(data).toBe('expected value');
    done();
  });
});

Мокирование в Jest

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

Мокирование функций

Чтобы замокировать функцию, используйте jest.fn():

const mockFunction = jest.fn();
mockFunction('argument');
expect(mockFunction).toHaveBeenCalledWith('argument');

Мокирование модулей

Jest также позволяет замещать целые модули. Для этого используется jest.mock().

jest.mock('./myModule', () => {
  return {
    myFunction: jest.fn(() => 'mocked value'),
  };
});

it('должен использовать замокированную функцию', () => {
  const result = myFunction();
  expect(result).toBe('mocked value');
});

Тестирование с использованием базы данных

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

Пример мокирования работы с базой данных:

jest.mock('mongoose');
const { User } = require('./models');

it('должен сохранять пользователя', async () => {
  const saveMock = jest.fn().mockResolvedValue(true);
  User.prototype.save = saveMock;

  const user = new User({ name: 'John' });
  const result = await user.save();

  expect(saveMock).toHaveBeenCalled();
  expect(result).toBe(true);
});

Покрытие кода тестами

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

Для включения покрытия нужно запустить Jest с параметром --coverage:

npm test -- --coverage

Jest создает отчет, в котором отображается процент покрытия, а также список строк, функций и веток, которые не были покрыты тестами.

Советы по написанию тестов

  1. Избегать тестирования реализации: Тесты должны проверять поведение приложения, а не его внутреннюю реализацию.
  2. Тестировать границы и крайние случаи: Включайте тесты для крайних случаев, таких как пустые входные данные или значения, выходящие за пределы допустимого диапазона.
  3. Использовать мокирование, чтобы изолировать код: Мокирование позволяет избежать зависимости от внешних сервисов или данных, что делает тесты более быстрыми и надежными.

Запуск тестов в CI/CD

Для того чтобы обеспечить надежность приложения на всех этапах разработки, тесты следует запускать в процессе CI/CD. Jest поддерживает интеграцию с большинством CI/CD сервисов, таких как GitHub Actions, GitLab CI и CircleCI. Для этого достаточно добавить команду для запуска тестов в конфигурацию пайплайна.

Пример для GitHub Actions:

name: Node.js CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14'
      - name: Install dependencies
        run: npm install
      - name: Run tests
        run: npm test

Заключение

Jest предоставляет мощные инструменты для эффективного тестирования Node.js приложений, начиная от простых юнит-тестов до сложных сценариев с мокированием и асинхронными операциями. Используя Jest, можно легко создать стабильные и поддерживаемые тесты для приложений, что позволяет повысить качество и надежность кода на всех этапах разработки.