Какие типы тестов ты писал в Node.js-проектах?

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

Ответ

В своих Node.js-проектах я применяю многоуровневое тестирование, обычно с использованием Jest или комбинации Mocha/Chai/Sinon.

1. Модульные (Unit) тесты: Тестирую изолированные функции, утилиты или классы. Для изоляции зависимостей использую моки (Jest или Sinon).

// tests/utils/calculator.test.js
const { calculateDiscount } = require('../../utils/calculator');

describe('calculateDiscount', () => {
  it('should apply 10% discount for premium users', () => {
    const result = calculateDiscount(100, 'premium');
    expect(result).toBe(90);
  });

  it('should throw error for negative price', () => {
    expect(() => calculateDiscount(-50, 'standard')).toThrow();
  });
});

2. Интеграционные тесты API: Тестирую взаимодействие с базой данных и внешними сервисами. Использую supertest для HTTP-запросов и тестовую базу данных (например, Docker-контейнер с PostgreSQL).

// tests/integration/user.api.test.js
const request = require('supertest');
const app = require('../../app');
const db = require('../../db');

beforeEach(async () => {
  await db.query('TRUNCATE TABLE users CASCADE'); // Очистка перед каждым тестом
});

describe('POST /api/users', () => {
  it('should create a new user', async () => {
    const res = await request(app)
      .post('/api/users')
      .send({ name: 'Alice', email: 'alice@test.com' });
    expect(res.statusCode).toBe(201);
    expect(res.body).toHaveProperty('id');
  });
});

3. Тестирование событий EventEmitter и потоков: Для сервисов, работающих с потоками (streams) или кастомными EventEmitter, пишу тесты, проверяющие корректность генерируемых событий и обработку данных.

4. Нагрузочное тестирование: Для критичных эндпоинтов использовал autocannon или artillery для проверки поведения системы под нагрузкой, что особенно важно для асинхронных операций в Node.js.

Ключевой практикой считаю изоляцию тестов (каждый тест не должен зависеть от состояния, оставленного другим) и покрытие не только "счастливого пути", но и обработки ошибок и пограничных случаев.