Как было организовано тестирование в вашем Node.js-проекте?

«Как было организовано тестирование в вашем Node.js-проекте?» — вопрос из категории Тестирование, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Мы использовали комбинацию разных уровней тестирования с Jest в качестве основного фреймворка.

1. Unit-тесты: Тестировали отдельные функции, модули и классы, изолируя их от внешних зависимостей с помощью моков (jest.mock()).

// service/userService.test.js
const userService = require('./userService');
const User = require('../models/User');

jest.mock('../models/User'); // Мокаем модель Mongoose

describe('User Service', () => {
  test('createUser should hash password', async () => {
    User.mockImplementation(() => ({
      save: jest.fn().mockResolvedValue({ _id: '123', email: 'test@mail.com' })
    }));

    const result = await userService.createUser('test@mail.com', 'plainPassword');
    expect(result.email).toBe('test@mail.com');
    expect(result.password).not.toBe('plainPassword'); // Проверяем, что пароль захэширован
  });
});

2. Интеграционные тесты: Проверяли взаимодействие нескольких модулей, например, сервиса с реальной (или in-memory) базой данных. Для этого поднимали тестовый экземпляр MongoDB с помощью mongodb-memory-server.

3. API (E2E) тесты: С помощью Supertest тестировали HTTP-эндпоинты нашего Express-приложения.

// tests/api/users.test.js
const request = require('supertest');
const app = require('../../app');
const { generateAuthToken } = require('../../utils/auth');

describe('GET /api/users', () => {
  test('should return 401 without token', async () => {
    const res = await request(app).get('/api/users');
    expect(res.statusCode).toBe(401);
  });

  test('should return user list for admin', async () => {
    const adminToken = generateAuthToken({ userId: '1', role: 'admin' });
    const res = await request(app)
      .get('/api/users')
      .set('Authorization', `Bearer ${adminToken}`);
    expect(res.statusCode).toBe(200);
    expect(Array.isArray(res.body)).toBe(true);
  });
});

Инфраструктура и инструменты:

  • Husky + lint-staged: Запуск линтера (ESLint) и юнит-тестов перед коммитом.
  • Jest coverage: Генерация отчета о покрытии кода тестами (npm test -- --coverage).
  • GitHub Actions: Автоматический прогон всей тестовой suites при каждом пуше в PR. Деплой на production происходил только после успешного прохождения всех тестов.