← Назад к вопросам
В чем разница между Unit и интеграционным тестом?
1.7 Middle🔥 181 комментариев
#Архитектура и паттерны#Тестирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между Unit и интеграционным тестом
Unit-тесты и интеграционные тесты - это два вида автоматизированного тестирования, которые работают на разных уровнях и тестируют разные аспекты вашего приложения.
Unit-тесты
Unit-тест проверяет отдельный компонент, функцию или модуль в изоляции от остального кода.
Характеристики:
- Тестирует одну функцию или компонент
- Использует мокирование (mocking) для зависимостей
- Быстрое выполнение
- Простая диагностика ошибок
- Узкий фокус на конкретной логике
- Требует подготовки mock-объектов
// Функция для тестирования
function calculateDiscount(price: number, discountPercent: number): number {
return price * (1 - discountPercent / 100);
}
// Unit-тест
describe('calculateDiscount', () => {
it('должна правильно вычислить скидку', () => {
const result = calculateDiscount(100, 20);
expect(result).toBe(80);
});
it('должна вернуть исходную цену при 0% скидке', () => {
const result = calculateDiscount(100, 0);
expect(result).toBe(100);
});
it('должна вернуть 0 при 100% скидке', () => {
const result = calculateDiscount(100, 100);
expect(result).toBe(0);
});
});
Unit-тест компонента React
import { render, screen } from '@testing-library/react';
import { Button } from './Button';
describe('Button Component', () => {
it('должна отрисовать кнопку с текстом', () => {
render(<Button>Клик</Button>);
const button = screen.getByRole('button', { name: /клик/i });
expect(button).toBeInTheDocument();
});
it('должна вызвать onClick обработчик при клике', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Клик</Button>);
const button = screen.getByRole('button');
button.click();
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
Unit-тест с мокированием зависимости
// Сервис
class UserService {
async getUser(id: number): Promise<User> {
// вызов API
}
}
// Компонент, зависящий от сервиса
class UserProfile {
constructor(private userService: UserService) {}
async loadUser(id: number): Promise<User> {
return this.userService.getUser(id);
}
}
// Unit-тест с мокированием
describe('UserProfile', () => {
it('должна загрузить пользователя', async () => {
// Мокируем сервис
const mockUserService = {
getUser: jest.fn().mockResolvedValue({
id: 1,
name: 'John',
email: 'john@example.com'
})
};
const profile = new UserProfile(mockUserService as any);
const user = await profile.loadUser(1);
expect(mockUserService.getUser).toHaveBeenCalledWith(1);
expect(user.name).toBe('John');
});
});
Интеграционные тесты
Интеграционный тест проверяет взаимодействие нескольких компонентов вместе.
Характеристики:
- Тестирует несколько модулей вместе
- Использует реальные или квазиреальные зависимости (или минимум мокирования)
- Медленнее, чем unit-тесты
- Сложнее диагностировать ошибки
- Тестирует реальные сценарии использования
- Проверяет взаимодействие между компонентами
// Интеграционный тест: пользователь вводит данные и отправляет форму
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { LoginForm } from './LoginForm';
import * as userService from './services/userService';
describe('LoginForm Integration', () => {
it('должна отправить форму и показать результат', async () => {
// Используем реальный (или почти реальный) сервис
jest.spyOn(userService, 'login').mockResolvedValue({
token: 'abc123',
user: { id: 1, name: 'John' }
});
render(<LoginForm onSuccess={jest.fn()} />);
// Заполняем форму
fireEvent.change(screen.getByLabelText(/email/i), {
target: { value: 'john@example.com' }
});
fireEvent.change(screen.getByLabelText(/password/i), {
target: { value: 'password123' }
});
// Отправляем форму
fireEvent.click(screen.getByRole('button', { name: /login/i }));
// Проверяем результат
await waitFor(() => {
expect(screen.getByText(/successfully logged in/i)).toBeInTheDocument();
});
});
});
Интеграционный тест с реальной БД
import { AppModule } from './app.module';
import { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
describe('User API Integration Tests', () => {
let app: INestApplication;
let userService: UserService;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [AppModule]
}).compile();
app = module.createNestApplication();
await app.init();
userService = module.get<UserService>(UserService);
});
afterAll(async () => {
await app.close();
});
it('должна создать пользователя и получить его из БД', async () => {
// Создаем пользователя через сервис
const user = await userService.create({
email: 'test@example.com',
name: 'Test User'
});
// Получаем пользователя из БД
const found = await userService.findById(user.id);
expect(found.email).toBe('test@example.com');
expect(found.name).toBe('Test User');
});
});
Сравнительная таблица
| Параметр | Unit-тест | Интеграционный тест |
|---|---|---|
| Масштаб | Одна функция/компонент | Несколько компонентов |
| Мокирование | Обширное | Минимальное |
| Скорость | Быстрая (миллисекунды) | Медленнее (секунды) |
| Диагностика | Легко найти ошибку | Сложнее определить причину |
| Реальность | Искусственные условия | Близко к реальному использованию |
| Количество | Много (большое покрытие) | Меньше (выборочно) |
| Цель | Правильность функции | Правильность взаимодействия |
Пирамида тестирования
E2E тесты (мало)
/ \
/ \
/ Интеграционные \
/ тесты (среднее) \
/____________________ \
/ Unit-тесты (много) \
/ \
/_____________________________\
Пирамида показывает соотношение:
- Unit-тесты - наибольшее количество, быстрые
- Интеграционные - среднее количество
- E2E - минимальное количество, самые медленные
Практический пример: система заказов
Unit-тесты
// Тест функции подсчета суммы
describe('calculateOrderTotal', () => {
it('должна корректно считать сумму товаров', () => {
const items = [
{ price: 100, quantity: 2 },
{ price: 50, quantity: 1 }
];
expect(calculateOrderTotal(items)).toBe(250);
});
});
// Тест компонента кнопки
describe('AddToCartButton', () => {
it('должна вызвать addToCart при клике', () => {
const addToCart = jest.fn();
render(<AddToCartButton item={item} onAddToCart={addToCart} />);
fireEvent.click(screen.getByRole('button'));
expect(addToCart).toHaveBeenCalled();
});
});
Интеграционные тесты
// Тест: пользователь добавляет товар в корзину через UI
describe('Shopping Cart Integration', () => {
it('должна добавить товар в корзину и обновить общую сумму', async () => {
render(<ShoppingApp />);
const item = screen.getByText('Product 1');
fireEvent.click(within(item).getByRole('button', { name: /add/i }));
await waitFor(() => {
expect(screen.getByText(/total: \$100/)).toBeInTheDocument();
});
});
});
// Тест: процесс оплаты с реальным API
describe('Payment Integration', () => {
it('должна обработать платеж и создать заказ', async () => {
const { getByText } = render(<CheckoutForm />);
// Заполняем данные
fireEvent.change(screen.getByLabelText(/card number/i), {
target: { value: '4111111111111111' }
});
// Отправляем
fireEvent.click(getByText(/pay/i));
// Проверяем результат
await waitFor(() => {
expect(screen.getByText(/order confirmed/i)).toBeInTheDocument();
});
});
});
Когда писать что
Unit-тесты для:
- Чистых функций (утилиты, формулы)
- Отдельных компонентов
- Бизнес-логики
- Сложных алгоритмов
Интеграционные тесты для:
- Взаимодействия компонентов
- Workflow-ов (формы, процессы)
- API endpoints
- Работы с БД
Лучшие практики
- Unit-тесты должны быть быстрыми и многочисленными
- Интеграционные должны фокусироваться на критичных путях
- Используйте fixtures и factories для подготовки данных
- Изолируйте тесты - каждый должен быть независимым
- Называйте тесты описательно: "should return discount when percentage is valid"
Заключение
- Unit-тесты - быстро проверяют отдельные части (кирпичики)
- Интеграционные - проверяют, как части работают вместе (стена из кирпичей)
Оба типа тестов критичны для качественного приложения. Unit-тесты ловят локальные ошибки, интеграционные - проблемы взаимодействия.