Тестирование с supertest
Автотест — код, который сам открывает страницы и проверяет ответы. Его запускают после каждой правки и за секунды узнают, не сломалось ли что-то. В Node.js встроен модуль node:test для запуска тестов, а пакет supertest отправляет запросы прямо в приложение Express, не занимая порт.
Установка
npm install --save-dev supertestФлаг --save-dev записывает пакет в зависимости для разработки: на рабочем сервере он не нужен.
Почему app.js отдельно
В уроке о структуре проекта приложение вынесено в src/app.js, а запуск — в src/server.js. Теперь это пригодится: тест импортирует готовое приложение, а supertest сам запускает его на время запроса.
Отдельная база для тестов
Тесты не должны трогать настоящие статьи. Создайте файл .env.test с базой в памяти — она исчезает после завершения тестов:
SESSION_SECRET=test-session-secret
JWT_SECRET=test-jwt-secret
DATABASE_FILE=:memory:И добавьте скрипт в package.json:
{
"scripts": {
"test": "node --env-file=.env.test --test"
}
}Команда node --test сама находит файлы, имя которых заканчивается на .test.js.
Первый тест
Создайте файл test/posts.test.js:
// test/posts.test.js
import { test } from 'node:test';
import assert from 'node:assert/strict';
import request from 'supertest';
import app from '../src/app.js';
test('список статей отвечает массивом', async () => {
const response = await request(app).get('/api/posts');
assert.equal(response.status, 200);
assert.ok(Array.isArray(response.body));
});request(app).get() отправляет запрос и возвращает ответ: код в status, разобранный JSON в body, HTML — в text. Модуль assert проверяет ожидания.
Запуск
npm testВ терминале появится отчёт: название теста с отметкой ✔, а внизу — число пройденных и упавших тестов. Если тест упал, отчёт покажет, какое значение ожидалось и какое пришло.
Ещё проверки
Тесты удобно писать на каждый важный случай — особенно на ошибки:
// test/posts.test.js
test('несуществующая статья даёт 404', async () => {
const response = await request(app).get('/api/posts/99999');
assert.equal(response.status, 404);
});
test('создание без токена запрещено', async () => {
const response = await request(app)
.post('/api/posts')
.send({ title: 'Тест', body: 'Текст' });
assert.equal(response.status, 401);
});
test('главная страница блога открывается', async () => {
const response = await request(app).get('/posts');
assert.equal(response.status, 200);
assert.match(response.text, /Все статьи/);
});Метод .send() с объектом отправляет тело как JSON и сам выставляет заголовок Content-Type.
Частая ошибка
Не вызывайте app.listen() в src/app.js. Иначе при импорте в тест запустится настоящий сервер: порт может оказаться занят другим процессом, а npm test не завершится после тестов, потому что сервер продолжает ждать запросы. Запуск должен быть только в src/server.js, который тесты не импортируют.