В чём разница между юнит-тестами и интеграционными тестами?
Юнит-тесты и интеграционные тесты — это два уровня автоматического тестирования, которые используются для проверки корректности работы кода, но различаются масштабом охвата и целью.
Юнит-тесты (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, они не тестируют всю систему «снаружи» (включая браузер, сеть и сервер), но дают уверенность в том, что основные модули взаимодействуют корректно между собой.