Supertest для JavaScript (Node.js): полное руководство с примерами

Онлайн-тренажер Python для начинающих

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

Начать курс

Supertest для JavaScript (Node.js): полное руководство с примерами

Добро пожаловать на PythonLib! В этой статье мы подробно разберём библиотеку Supertest — один из самых популярных инструментов для HTTP-тестирования в экосистеме Node.js. Если вы разрабатываете веб-приложения на Express, Koa, Fastify или любом другом фреймворке, Supertest станет вашим незаменимым помощником для написания надёжных интеграционных тестов.

1. Что это и зачем нужна

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

Зачем нужна Supertest? В современной веб-разработке качество кода имеет первостепенное значение. Интеграционные тесты, проверяющие работу ваших API-эндпоинтов, помогают выявить регрессии, ошибки в обработке запросов и проблемы с валидацией данных. Supertest позволяет писать такие тесты лаконично и читаемо, используя цепочки методов (chaining) и встроенные ассерты для проверки статусов, заголовков, тела ответа и многого другого. Библиотека идеально сочетается с популярными тестовыми раннерами, такими как Jest, Mocha, Jasmine и AVA.

Библиотека была создана TJ Holowaychuk (автором Express, Koa и многих других известных проектов) и с тех пор поддерживается сообществом. Supertest построена поверх библиотеки SuperAgent — мощного HTTP-клиента для Node.js и браузера. Это значит, что вы получаете доступ ко всем возможностям SuperAgent, включая поддержку различных типов данных, файловые загрузки, работу с куками и многое другое.

2. Установка

Установка Supertest осуществляется через менеджер пакетов npm. Рекомендуется устанавливать библиотеку как зависимость для разработки (devDependency), так как она не требуется в production-среде.

npm install --save-dev supertest

Если вы используете yarn:

yarn add --dev supertest

Для работы с TypeScript вам также может понадобиться установка типов:

npm install --save-dev @types/supertest

Обратите внимание, что Supertest требует Node.js версии 14 или выше. Также убедитесь, что у вас установлен тестовый раннер (например, Jest или Mocha).

3. Быстрый старт — минимальный рабочий пример

Давайте создадим простейшее Express-приложение и напишем для него тест с использованием Supertest. Этот пример покажет базовую механику работы библиотеки.

Сначала создадим файл app.js с нашим приложением:

// app.js
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.status(200).json({ message: 'Hello, World!' });
});

app.get('/user', (req, res) => {
  res.status(200).json({ name: 'John Doe', age: 30 });
});

module.exports = app;

Теперь создадим тестовый файл app.test.js:

// app.test.js
const request = require('supertest');
const app = require('./app');

describe('GET /', () => {
  it('should return Hello, World!', async () => {
    const response = await request(app)
      .get('/')
      .expect(200);

    expect(response.body).toEqual({ message: 'Hello, World!' });
  });
});

describe('GET /user', () => {
  it('should return user data', async () => {
    const response = await request(app)
      .get('/user')
      .expect('Content-Type', /json/)
      .expect(200);

    expect(response.body.name).toBe('John Doe');
    expect(response.body.age).toBe(30);
  });
});

Для запуска тестов с Jest добавьте в package.json скрипт:

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

Запустите тесты командой npm test. Вы увидите, что оба теста проходят успешно. Обратите внимание, что мы не запускали сервер на реальном порту — Supertest сделал это за нас автоматически.

4. Основные методы/функции/классы

Supertest предоставляет богатый API для тестирования HTTP-эндпоинтов. Рассмотрим основные методы, которые вам понадобятся в повседневной работе.

4.1. request(app)

Сигнатура: request(app) ⇒ SuperTest

Описание: Основная функция, которая принимает экземпляр вашего приложения (или HTTP-сервер) и возвращает объект для построения запросов. Это точка входа в библиотеку.

Пример:

const request = require('supertest');
const app = require('./app');

const testRequest = request(app);
// Теперь можно использовать testRequest для отправки запросов

4.2. .get(path), .post(path), .put(path), .delete(path), .patch(path)

Сигнатура: .get(path) ⇒ Test, .post(path) ⇒ Test, и т.д.

Описание: Методы для указания HTTP-метода и пути запроса. Возвращают объект Test, который можно дополнительно настроить.

Пример:

// GET запрос
const response = await request(app)
  .get('/api/users')
  .expect(200);

// POST запрос
const response = await request(app)
  .post('/api/users')
  .send({ name: 'Jane', age: 25 })
  .expect(201);

4.3. .send(data)

Сигнатура: .send(data) ⇒ Test

Описание: Отправляет тело запроса. Может принимать объект, строку, Buffer или Stream. Автоматически устанавливает заголовок Content-Type (например, application/json для объектов).

Пример:

// Отправка JSON
await request(app)
  .post('/api/login')
  .send({ username: 'admin', password: 'secret' })
  .expect(200);

// Отправка формы
await request(app)
  .post('/api/upload')
  .send('key=value&foo=bar')
  .set('Content-Type', 'application/x-www-form-urlencoded')
  .expect(200);

4.4. .set(field, value) или .set(object)

Сигнатура: .set(field, value) ⇒ Test или .set(object) ⇒ Test

Описание: Устанавливает HTTP-заголовки запроса. Можно передавать как пару ключ-значение, так и объект с несколькими заголовками.

Пример:

// Установка одного заголовка
await request(app)
  .get('/api/protected')
  .set('Authorization', 'Bearer token123')
  .expect(200);

// Установка нескольких заголовков
await request(app)
  .get('/api/data')
  .set({
    'Accept': 'application/json',
    'X-Custom-Header': 'custom-value'
  })
  .expect(200);

4.5. .expect(status)

Сигнатура: .expect(status) ⇒ Test, .expect(status, body) ⇒ Test, .expect(function(res) {}) ⇒ Test

Описание: Проверяет ответ сервера. Может проверять HTTP-статус, тело ответа или выполнять произвольную функцию проверки. Это основной метод для ассертов.

Пример:

// Проверка только статуса
await request(app)
  .get('/api/health')
  .expect(200);

// Проверка статуса и тела
await request(app)
  .get('/api/user/1')
  .expect(200, { id: 1, name: 'John' });

// Произвольная проверка
await request(app)
  .get('/api/users')
  .expect((res) => {
    if (!Array.isArray(res.body)) {
      throw new Error('Response body should be an array');
    }
    if (res.body.length === 0) {
      throw new Error('Response body should not be empty');
    }
  });

4.6. .expect(header, value)

Сигнатура: .expect(header, value) ⇒ Test

Описание: Проверяет значение конкретного HTTP-заголовка в ответе. Значение может быть строкой или регулярным выражением.

Пример:

// Проверка Content-Type
await request(app)
  .get('/api/data')
  .expect('Content-Type', /json/);

// Проверка кастомного заголовка
await request(app)
  .get('/api/version')
  .expect('X-API-Version', '2.0');

4.7. .auth(username, password) или .auth(token, {type: 'bearer'})

Сигнатура: .auth(username, password) ⇒ Test или .auth(token, {type: 'bearer'}) ⇒ Test

Описание: Добавляет HTTP Basic или Bearer аутентификацию к запросу. Удобно для тестирования защищённых эндпоинтов.

Пример:

// Basic Auth
await request(app)
  .get('/api/admin')
  .auth('admin', 'password123')
  .expect(200);

// Bearer Token
await request(app)
  .get('/api/profile')
  .auth('eyJhbGciOiJIUzI1NiIs...', { type: 'bearer' })
  .expect(200);

4.8. .query(params)

Сигнатура: .query(params) ⇒ Test

Описание: Добавляет query-параметры к URL запроса. Принимает объект, который будет преобразован в строку запроса.

Пример:

// Добавление query параметров
await request(app)
  .get('/api/users')
  .query({ page: 1, limit: 10, sort: 'name' })
  .expect(200);
// Результат: GET /api/users?page=1&limit=10&sort=name

// Можно также передать строку
await request(app)
  .get('/api/search')
  .query('q=test&lang=en')
  .expect(200);

4.9. .attach(field, file) и .field(name, value)

Сигнатура: .attach(field, filepath) ⇒ Test, .attach(field, buffer, filename) ⇒ Test, .field(name, value) ⇒ Test

Описание: Используется для тестирования загрузки файлов (multipart/form-data). .attach добавляет файл, .field добавляет текстовые поля.

Пример:

const path = require('path');

// Загрузка файла с диска
await request(app)
  .post('/api/upload')
  .attach('avatar', path.join(__dirname, 'test-avatar.jpg'))
  .field('description', 'Test avatar')
  .expect(200);

// Загрузка из буфера
const buffer = Buffer.from('fake image content');
await request(app)
  .post('/api/upload')
  .attach('document', buffer, 'document.txt')
  .expect(200);

4.10. .redirects(n)

Сигнатура: .redirects(n) ⇒ Test

Описание: Управляет следованием по редиректам. По умолчанию Supertest не следует редиректам. Вызов .redirects(0) отключает следование, .redirects(n) позволяет следовать до n редиректов.

Пример:

// Не следовать редиректу
await request(app)
  .get('/old-page')
  .redirects(0)
  .expect(301);

// Следовать до 5 редиректов
await request(app)
  .get('/redirect-chain')
  .redirects(5)
  .expect(200);

4.11. .timeout(ms)

Сигнатура: .timeout(ms) ⇒ Test

Описание: Устанавливает таймаут для запроса в миллисекундах. Если ответ не получен за указанное время, запрос будет прерван.

Пример:

// Установка таймаута в 5 секунд
await request(app)
  .get('/api/slow-operation')
  .timeout(5000)
  .expect(200);

4.12. .buffer() и .parse()

Сигнатура: .buffer() ⇒ Test, .parse(fn) ⇒ Test

Описание: .buffer() включает буферизацию ответа (полезно для больших ответов). .parse() позволяет задать кастомную функцию для парсинга ответа.

Пример:

// Включение буферизации
await request(app)
  .get('/api/large-data')
  .buffer()
  .expect(200);

// Кастомный парсер
await request(app)
  .get('/api/custom-format')
  .parse((res, callback) => {
    let data = '';
    res.on('data', chunk => data += chunk);
    res.on('end', () => {
      callback(null, JSON.parse(data));
    });
  })
  .expect(200                    

Рекомендаций