Як протестувати додаток Express.js?
Тестування додатків Express.js
Тестування додатків Express передбачає модульні тести для окремих функцій та інтеграційні тести для повного циклу запиту/відповіді HTTP. Ключовим інструментом є Supertest — він здійснює реальні HTTP запити до вашого додатку без запуску сервера.
Налаштування
bash
npm install --save-dev jest supertest
# або з TypeScript:
npm install --save-dev jest supertest @types/jest @types/supertest ts-jestСтруктура додатку для тестування
Відокремте ваш додаток Express від запуску сервера:
js
// app.js — експортуйте app, без listen()
const express = require('express');
const app = express();
app.use(express.json());
app.use('/users', usersRouter);
module.exports = app;
// server.js — тільки цей файл викликає listen()
const app = require('./app');
app.listen(3000);Інтеграційні тести з Supertest
js
// users.test.js
const request = require('supertest');
const app = require('../app');
describe('Users API', () => {
describe('GET /users', () => {
it('повинен повернути 200 та масив користувачів', async () => {
const res = await request(app).get('/users');
expect(res.status).toBe(200);
expect(res.body).toHaveProperty('data');
expect(Array.isArray(res.body.data)).toBe(true);
});
});
describe('POST /users', () => {
it('повинен створити користувача та повернути 201', async () => {
const res = await request(app)
.post('/users')
.send({ name: 'Alice', email: 'alice@test.com' });
expect(res.status).toBe(201);
expect(res.body.data.name).toBe('Alice');
});
it('повинен повернути 400, коли відсутній email', async () => {
const res = await request(app)
.post('/users')
.send({ name: 'Alice' }); // без email!
expect(res.status).toBe(400);
expect(res.body).toHaveProperty('error');
});
});
describe('GET /users/:id', () => {
it('повинен повернути 404 для неіснуючого користувача', async () => {
const res = await request(app).get('/users/9999');
expect(res.status).toBe(404);
});
});
});Тестування захищених маршрутів
js
const jwt = require('jsonwebtoken');
function generateTestToken(payload = { id: 1, role: 'user' }) {
return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1h' });
}
describe('Захищені маршрути', () => {
it('повинен повернути 401 без токена', async () => {
const res = await request(app).get('/profile');
expect(res.status).toBe(401);
});
it('повинен повернути 200 з дійсним токеном', async () => {
const token = generateTestToken();
const res = await request(app)
.get('/profile')
.set('Authorization', `Bearer ${token}`);
expect(res.status).toBe(200);
});
});Мокінг сервісів
js
// Мокінг шару бази даних
jest.mock('../services/users.service', () => ({
findAll: jest.fn().mockResolvedValue([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]),
findById: jest.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
create: jest.fn().mockResolvedValue({ id: 3, name: 'Charlie' })
}));
const usersService = require('../services/users.service');
it('повинен викликати service.findAll один раз', async () => {
await request(app).get('/users');
expect(usersService.findAll).toHaveBeenCalledTimes(1);
});Стратегія тестової бази даних
| Підхід | Швидкість | Ізоляція | Реалістичність |
|---|---|---|---|
| Мокінг шару сервісів | Найшвидший | Висока | Низька |
| База даних в пам'яті (SQLite) | Швидка | Середня | Середня |
| Тестова база даних | Повільніша | Низька (потребує очищення) | Висока |
| Контейнер Docker | Найповільніший | Висока | Найвища |
jest.config.js
js
module.exports = {
testEnvironment: 'node',
setupFilesAfterEach: ['./jest.setup.js'],
coveragePathIgnorePatterns: ['/node_modules/'],
};Резюме
Використовуйте Supertest + Jest для тестування додатків Express. Тримайте ваш додаток у app.js (без listen()) для того, щоб тести могли імпортувати його безпосередньо. Тестуйте повний цикл HTTP: коди статусу, тіло відповіді, заголовки та аутентифікацію. Мокінг шару бази даних/сервісів для швидких, ізольованих модульних тестів.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.