Пошук уроків, статей та іншого контенту
Використаєте Supertest для надсилання запитів до NestJS і перевірки статусів, заголовків та відповідей.
Supertest — бібліотека для тестування HTTP-серверів. Вона дає змогу надсилати запити до NestJS-застосунку та перевіряти:
HTTP-статус відповіді;
заголовки;
тіло відповіді;
JSON-дані;
поведінку для неіснуючих маршрутів або неправильних запитів.
У NestJS Supertest найчастіше використовують в інтеграційних або end-to-end-тестах. На відміну від unit-тестів, такі тести перевіряють маршрут через HTTP-рівень: від виклику URL до формування відповіді контролером.
У типовому NestJS-проєкті Supertest уже доданий серед dev-залежностей. Якщо бібліотеки немає, встановіть її:
npm install --save-dev supertest @types/supertestДля прикладу використаємо простий контролер стану сервісу:
// src/health.controller.ts
import { Controller, Get, Header } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
@Header('X-Service-Version', '1')
getHealth() {
return {
status: 'ok',
};
}
}Підключимо контролер у модулі:
// src/app.module.ts
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class AppModule {}Маршрут GET /health повинен повертати:
статус 200;
JSON із полем status;
заголовок X-Service-Version: 1.
End-to-end-тести NestJS зазвичай розміщують у каталозі test. Створимо файл test/app.e2e-spec.ts:
import { INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
describe('Health API (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
it('повертає стан сервісу та правильні заголовки', async () => {
await request(app.getHttpServer())
.get('/health')
.expect(200)
.expect('Content-Type', /json/)
.expect('X-Service-Version', '1')
.expect({
status: 'ok',
});
});
it('повертає 404 для невідомого маршруту', async () => {
await request(app.getHttpServer())
.get('/unknown-route')
.expect(404);
});
});Розглянемо основні частини тесту:
Test.createTestingModule() створює тестовий модуль NestJS.
createNestApplication() створює екземпляр HTTP-застосунку.
app.init() запускає застосунок у тестовому середовищі.
app.getHttpServer() повертає HTTP-сервер, до якого підключається Supertest.
app.close() зупиняє застосунок після завершення всіх тестів.
Тест не відкриває реальний порт. Supertest працює безпосередньо з HTTP-сервером NestJS у пам’яті.
Метод .expect() можна використовувати для перевірки статусу відповіді:
await request(app.getHttpServer())
.get('/health')
.expect(200);Якщо сервер поверне інший статус, тест завершиться помилкою.
Для перевірки помилкових сценаріїв можна очікувати статус 404, 400, 401 або інший статус, який передбачає API:
await request(app.getHttpServer())
.get('/unknown-route')
.expect(404);Перевірка статусу важлива, оскільки правильне тіло відповіді саме по собі не гарантує правильну HTTP-поведінку.
Заголовок можна перевірити, передавши його назву та очікуване значення:
await request(app.getHttpServer())
.get('/health')
.expect('X-Service-Version', '1');Для заголовків, значення яких може містити додаткові параметри, зручно використовувати регулярний вираз:
await request(app.getHttpServer())
.get('/health')
.expect('Content-Type', /json/);Наприклад, фактичне значення Content-Type може бути таким:
application/json; charset=utf-8Регулярний вираз /json/ перевіряє, що заголовок містить потрібний фрагмент, не вимагаючи повного збігу рядка.
Якщо endpoint повертає JSON, Supertest розбирає його та робить доступним для перевірки.
Повну відповідь можна порівняти з очікуваним об’єктом:
await request(app.getHttpServer())
.get('/health')
.expect({
status: 'ok',
});Також можна отримати відповідь у змінну та перевірити окремі поля:
it('повертає правильне тіло відповіді', async () => {
const response = await request(app.getHttpServer())
.get('/health')
.expect(200);
expect(response.body.status).toBe('ok');
});Об’єкт response містить, зокрема:
response.status — числовий HTTP-статус;
response.headers — заголовки;
response.body — розібране тіло відповіді;
response.text — тіло відповіді у вигляді тексту.
Такий підхід корисний, коли потрібно перевірити лише частину великого JSON:
const response = await request(app.getHttpServer())
.get('/health')
.expect(200);
expect(response.body).toEqual(
expect.objectContaining({
status: 'ok',
}),
);Методи Supertest можна послідовно об’єднувати в один ланцюжок:
await request(app.getHttpServer())
.get('/health')
.expect(200)
.expect('Content-Type', /json/)
.expect('X-Service-Version', '1')
.expect({ status: 'ok' });Такий тест одночасно перевіряє:
HTTP-метод і маршрут;
статус відповіді;
тип даних;
власний заголовок;
тіло відповіді.
Якщо будь-яка перевірка не пройде, Jest позначить тест як невдалий.
Supertest також дає змогу додавати заголовки до запиту за допомогою .set():
await request(app.getHttpServer())
.get('/health')
.set('Accept', 'application/json')
.expect(200);Це потрібно для тестування endpoint, які залежать від заголовків запиту, наприклад Authorization, Accept або власних заголовків клієнта.
Метод .set() приймає назву заголовка та його значення:
.set('X-Client-Id', 'test-client')Для HTTP-методів, які приймають дані, використовується .send():
await request(app.getHttpServer())
.post('/users')
.send({
name: 'Olena',
email: 'olena@example.com',
})
.expect(201);Якщо передати об’єкт JavaScript, Supertest зазвичай серіалізує його як JSON. У тесті можна перевірити як статус, так і відповідь:
const response = await request(app.getHttpServer())
.post('/users')
.send({
name: 'Olena',
email: 'olena@example.com',
})
.expect(201);
expect(response.body.email).toBe('olena@example.com');Для такого тесту в застосунку повинен існувати маршрут POST /users. Не слід тестувати лише Supertest — endpoint має відповідати контракту, який перевіряє тест.
Query-параметри можна додати через .query():
await request(app.getHttpServer())
.get('/users')
.query({
page: 2,
limit: 10,
})
.expect(200);Supertest сформує URL із параметрами:
/users?page=2&limit=10Це дає змогу перевіряти фільтрацію, пагінацію та інші параметри HTTP-запитів.
У проєктах NestJS зазвичай використовують одну з таких команд:
npm run test:e2eАбо безпосередньо Jest:
npx jest --config ./test/jest-e2e.jsonТочна команда залежить від налаштувань package.json. Важливо, щоб конфігурація Jest знаходила файли з розширенням .e2e-spec.ts.
Перед запуском перевірте, що:
модуль із потрібними контролерами імпортується в тест;
застосунок ініціалізується через app.init();
після тестів викликається app.close();
шлях до AppModule відповідає структурі проєкту.
awaitHTTP-запит Supertest повертає Promise. Якщо не використати await, Jest може завершити тест раніше, ніж буде отримана відповідь:
// Неправильно
request(app.getHttpServer())
.get('/health')
.expect(200);Правильний варіант:
await request(app.getHttpServer())
.get('/health')
.expect(200);До надсилання запитів потрібно викликати:
await app.init();Без цього модулі, маршрути та middleware можуть бути не готові до роботи.
Якщо контролер має префікс:
@Controller('health')і метод позначений:
@Get()повний маршрут буде:
GET /healthЯкщо в main.ts встановлено глобальний префікс:
app.setGlobalPrefix('api');тоді в тесті потрібно використовувати:
.get('/api/health')Якщо endpoint повертає JSON, перевіряйте response.body, а не response.text:
const response = await request(app.getHttpServer())
.get('/health')
.expect(200);
expect(response.body.status).toBe('ok');Якщо не викликати app.close(), Jest може залишити відкриті ресурси й показати попередження про незавершені handles:
afterAll(async () => {
await app.close();
});Supertest використовується для перевірки HTTP-поведінки NestJS-застосунку.
Запити надсилаються через request(app.getHttpServer()).
Метод .get(), .post() та інші задають HTTP-метод і маршрут.
.expect() перевіряє статус, заголовки та тіло відповіді.
response.body, response.headers і response.status дають доступ до окремих частин відповіді.
.set() додає заголовки запиту.
.send() передає тіло запиту.
.query() додає query-параметри.
Тестовий NestJS-застосунок потрібно ініціалізувати перед тестами та закрити після них.