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