← Назад к вопросам

В чем разница между 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
  • Работы с БД

Лучшие практики

  1. Unit-тесты должны быть быстрыми и многочисленными
  2. Интеграционные должны фокусироваться на критичных путях
  3. Используйте fixtures и factories для подготовки данных
  4. Изолируйте тесты - каждый должен быть независимым
  5. Называйте тесты описательно: "should return discount when percentage is valid"

Заключение

  • Unit-тесты - быстро проверяют отдельные части (кирпичики)
  • Интеграционные - проверяют, как части работают вместе (стена из кирпичей)

Оба типа тестов критичны для качественного приложения. Unit-тесты ловят локальные ошибки, интеграционные - проблемы взаимодействия.

В чем разница между Unit и интеграционным тестом? | PrepBro