В чём разница между юнит-тестами и интеграционными тестами?

Юнит-тесты и интеграционные тесты — это два уровня автоматического тестирования, которые используются для проверки корректности работы кода, но различаются масштабом охвата и целью.

Юнит-тесты (Unit tests)

Юнит-тесты (модульные тесты) проверяют отдельные, атомарные части кода — обычно это функции, методы или классы. Их основная задача — убедиться, что конкретный блок логики работает правильно независимо от остальной системы.

Ключевые характеристики:

  • Тестируют один юнит — минимальную тестируемую часть приложения (например, одну функцию).

  • Изолированы от других частей системы (могут использовать моки, стабы, заглушки).

  • Быстро выполняются.

  • Не требуют работы с сетью, базой данных или UI.

  • Хорошо подходят для TDD (разработка через тестирование).

  • Часто используются в процессах CI/CD.

Примеры:

// Пример юнит-теста функции
function sum(a, b) {
return a + b;
}
// Jest unit test
test('sum adds two numbers', () => {
expect(sum(2, 3)).toBe(5);
});

Библиотеки:

  • Jest

  • Vitest

  • Mocha + Chai

  • Jasmine

Интеграционные тесты (Integration tests)

Интеграционные тесты проверяют взаимодействие между модулями или компонентами. Основная задача — убедиться, что несколько частей системы работают корректно вместе, как ожидается.

Ключевые характеристики:

  • Тестируют связь между несколькими юнитами — например, как компонент React взаимодействует с API или глобальным состоянием.

  • Менее изолированы (часто задействованы реальные зависимости или частичные моки).

  • Более медленные, чем юнит-тесты.

  • Могут включать асинхронные вызовы.

  • Позволяют находить ошибки в стыках компонентов.

Примеры:

// Компонент
function UserProfile({ userId }) {
const \[user, setUser\] = useState(null);
useEffect(() => {
fetch(\`/api/users/${userId}\`)
.then(res => res.json())
.then(data => setUser(data));
}, \[userId\]);
if (!user) return <p>Loading...</p>;
return <p>{user.name}</p>;
}
// Integration test (React Testing Library)
import { render, screen, waitFor } from '@testing-library/react';
test('UserProfile fetches and displays user data', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ name: 'Alice' }),
})
);
render(<UserProfile userId="123" />);
await waitFor(() => expect(screen.getByText('Alice')).toBeInTheDocument());
});

Библиотеки:

  • React Testing Library

  • Cypress (e2e + integration)

  • Playwright (может использоваться как интеграционный инструмент)

  • Jest (может использоваться для интеграционных тестов)

Сравнение по признакам:

Характеристика Юнит-тесты Интеграционные тесты
Цель Проверка конкретной функции Проверка взаимодействия компонентов
--- --- ---
Изоляция Полная (используются моки) Частичная или отсутствует
--- --- ---
Скорость Очень высокая Средняя или низкая
--- --- ---
Стоимость написания Низкая Выше, чем у юнит-тестов
--- --- ---
Уровень детализации Очень высокий Средний
--- --- ---
Обнаруживаемые ошибки Логика функций Проблемы интеграции, API, состояние
--- --- ---
Пример использования Функция сложения чисел Форма, отправляющая данные в API
--- --- ---

Дополнительно:

  • В React-проектах часто встречается смешанная форма: компонент тестируется с настоящими хуками и контекстом, но при этом API мокаются — это интеграционные тесты на уровне компонента.

  • Интеграционные тесты не заменяют юнит-тесты — их нужно использовать совместно, так как они покрывают разные уровни риска.

  • Юнит-тесты легче локализуют проблему: если один тест падает, ты сразу знаешь, где ошибка. Интеграционные тесты могут проваливаться по множеству причин (в том числе сторонним).

Связь с E2E тестами:

Интеграционные тесты занимают средний уровень между юнит-тестами и end-to-end (e2e) тестами. В отличие от e2e, они не тестируют всю систему «снаружи» (включая браузер, сеть и сервер), но дают уверенность в том, что основные модули взаимодействуют корректно между собой.