Тестирование в JavaScript: от модульных до сквозных тестов
Качественное тестирование — неотъемлемая часть профессиональной разработки на JavaScript. В этой статье мы разберём три основных уровня тестирования: модульные (unit), интеграционные (integration) и сквозные (e2e) тесты. Вы узнаете, какие инструменты использовать, как писать тесты на Node.js и как построить эффективную стратегию тестирования.
1. Unit-тестирование: основа пирамиды тестирования
Unit-тесты проверяют изолированные модули — функции, классы или отдельные методы. Главное правило: тестируемый код не должен зависеть от внешних ресурсов (базы данных, API, файловой системы).
Инструменты для unit-тестирования
- Jest — самый популярный фреймворк от Facebook. Из коробки включает моки, снапшоты и покрытие кода.
- Mocha + Chai — гибкая связка для тех, кто любит настраивать всё вручную.
- Vitest — современный аналог Jest, оптимизированный для Vite-проектов.
// Пример unit-теста на Jest
const { calculateDiscount } = require('./pricing');
describe('calculateDiscount', () => {
test('возвращает 10% скидку для суммы > 1000', () => {
const result = calculateDiscount(1500);
expect(result).toBe(1350);
});
test('не применяет скидку для суммы <= 1000', () => {
const result = calculateDiscount(500);
expect(result).toBe(500);
});
test('обрабатывает граничное значение 1000', () => {
const result = calculateDiscount(1000);
expect(result).toBe(1000);
});
});
Лучшие практики unit-тестирования
- Тестируйте одно поведение за раз (один assert на тест).
- Используйте описательные названия тестов (given/when/then).
- Избегайте тестирования приватных методов — тестируйте публичный интерфейс.
- Поддерживайте покрытие кода на уровне 70-80%.
2. Integration-тестирование: проверка взаимодействия компонентов
Интеграционные тесты проверяют, как различные модули работают вместе. В контексте Node.js это обычно означает тестирование с реальной базой данных, файловой системой или внешними API.
Типичные сценарии интеграционного тестирования
- Взаимодействие контроллера с сервисным слоем и базой данных.
- Проверка middleware и маршрутов Express/Koa.
- Тестирование репозиториев с тестовой БД (SQLite в памяти или PostgreSQL в Docker).
// Пример интеграционного теста с Supertest и Jest
const request = require('supertest');
const app = require('../app');
const { createTestUser, cleanDatabase } = require('./helpers');
beforeEach(async () => {
await cleanDatabase();
});
describe('POST /api/users', () => {
test('создаёт пользователя и возвращает 201', async () => {
const response = await request(app)
.post('/api/users')
.send({ name: 'Иван', email: 'ivan@test.com' });
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('id');
expect(response.body.name).toBe('Иван');
});
test('возвращает 400 при дублировании email', async () => {
await createTestUser({ email: 'ivan@test.com' });
const response = await request(app)
.post('/api/users')
.send({ name: 'Петр', email: 'ivan@test.com' });
expect(response.status).toBe(400);
expect(response.body.error).toBe('Email уже используется');
});
});
Настройка тестовой инфраструктуры
- Docker Compose — для поднятия тестовых сервисов (БД, Redis).
- Testcontainers — программное управление контейнерами из тестов.
- In-memory базы данных (например, mongodb-memory-server) — для быстрых тестов без внешних зависимостей.
3. E2E-тестирование: проверка системы целиком
Сквозные (end-to-end) тесты имитируют действия реального пользователя. Они проверяют всю систему от интерфейса до базы данных. Для веб-приложений это означает запуск браузера и автоматизацию кликов, ввода данных и навигации.
Популярные инструменты для E2E
- Cypress — современный инструмент с отличным UI, автоматическими ожиданиями и возможностью записи тестов.
- Playwright — от Microsoft, поддерживает все браузеры (Chrome, Firefox, Safari) и мобильные эмуляторы.
- Puppeteer — библиотека для управления Chromium, низкоуровневая, но гибкая.
// Пример E2E теста на Cypress
describe('Авторизация пользователя', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/login');
});
it('успешный вход с корректными данными', () => {
cy.get('[data-testid="email-input"]').type('user@example.com');
cy.get('[data-testid="password-input"]').type('correct-password');
cy.get('[data-testid="login-button"]').click();
cy.url().should('include', '/dashboard');
cy.contains('Добро пожаловать, User');
});
it('показывает ошибку при неверном пароле', () => {
cy.get('[data-testid="email-input"]').type('user@example.com');
cy.get('[data-testid="password-input"]').type('wrong-password');
cy.get('[data-testid="login-button"]').click();
cy.contains('Неверный email или пароль');
});
});
Когда писать E2E тесты
- Критически важные пользовательские сценарии (регистрация, оплата, поиск).
- Регрессионное тестирование перед релизом.
- Проверка интеграции с внешними сервисами (платёжные шлюзы, OAuth).
4. Стратегия тестирования: как всё совместить
Идеальная стратегия основывается на пирамиде тестирования, предложенной Майком Коном:
- Основание (70%) — быстрые unit-тесты. Их должно быть много.
- Середина (20%) — интеграционные тесты. Проверяют ключевые сценарии взаимодействия.
- Вершина (10