Чем принципиально отличаются unit-тесты от интеграционных тестов?csharp-12

Основные различия

ХарактеристикаUnit-тестыИнтеграционные тесты
Объект тестированияОтдельный класс/методГруппа взаимодействующих компонентов
ИзоляцияПолная (моки/стабы)Частичная или отсутствует
Скорость выполненияОчень быстрые (мс)Медленные (секунды-минуты)
ЗависимостиЗамоканы всеИспользуются реальные
Цель тестированияПроверка логикиПроверка взаимодействия
Частота запускаПри каждом коммитеПеред релизом/ночью
Покрытие70-80% кодаКритические пути

Детальное объяснение с примерами на C#

1. Unit-тесты

Характеристики:

  • Тестируют один "юнит" (метод, класс) в изоляции
  • Все внешние зависимости заменяются mock-объектами
  • Быстрые и детерминированные

Пример теста для сервиса калькулятора:

[TestClass]
public class CalculatorTests
{
    [TestMethod]
    public void Add_TwoNumbers_ReturnsSum()
    {
        // Arrange
        var calculator = new Calculator();

        // Act
        var result = calculator.Add(3, 5);

        // Assert
        Assert.AreEqual(8, result);
    }
}

Когда использовать:

  • Тестирование бизнес-логики
  • Проверка алгоритмов
  • Валидация входных параметров

2. Интеграционные тесты

Характеристики:

  • Тестируют взаимодействие нескольких компонентов
  • Используют реальные базы данных, сервисы
  • Требуют настройки окружения

Пример теста API с базой данных:

[TestClass]
public class OrdersIntegrationTests
{
    private TestServer _server;
    private HttpClient _client;

    [TestInitialize]
    public void Setup()
    {
        _server = new TestServer(WebHost.CreateDefaultBuilder()
            .UseStartup<TestStartup>());
        _client = _server.CreateClient();
    }

    [TestMethod]
    public async Task CreateOrder_ValidRequest_ReturnsCreatedOrder()
    {
        // Arrange
        var request = new { ProductId = 1, Quantity = 2 };

        // Act
        var response = await _client.PostAsJsonAsync("/api/orders", request);

        // Assert
        response.EnsureSuccessStatusCode();
        var order = await response.Content.ReadAsAsync<Order>();
        Assert.IsTrue(order.Id > 0);
    }

    [TestCleanup]
    public void Cleanup()
    {
        _client.Dispose();
        _server.Dispose();
    }
}

Когда использовать:

  • Тестирование работы с БД
  • Проверка интеграции с внешними API
  • Тестирование полного workflow

Ключевые отличия на практике

1. Подготовка контекста

  • Unit: Mock-объекты (Moq, NSubstitute)
var mockRepository = new Mock<IUserRepository>();
mockRepository.Setup(x => x.GetById(1)).Returns(new User { Id = 1 });
  • Интеграционные: Реальные сервисы + Test Containers
var container = new TestcontainersBuilder<PostgreSqlTestcontainer>()
    .WithDatabase(new PostgreSqlTestcontainerConfiguration())
    .Build();
await container.StartAsync();

2. Время выполнения

  • Unit-тесты: 1000+ тестов за секунды
  • Интеграционные: 50-100 тестов за минуты

3. Надежность

  • Unit: Стабильны (нет внешних факторов)
  • Интеграционные: Могут флапать из-за временных проблем сети/БД

Практические рекомендации

  1. Пирамида тестирования:

    • 70% unit
    • 20% интеграционные
    • 10% end-to-end
  2. Паттерны:

    • Для unit: AAA (Arrange-Act-Assert)
    • Для интеграционных: Setup-Exercise-Verify-Teardown
  3. Инструменты:

    • Unit: xUnit/NUnit + Moq
    • Интеграционные: xUnit + WebApplicationFactory + TestContainers

Резюмируем

Unit-тесты:

  • Тестируют изолированные единицы кода
  • Быстрые и стабильные
  • Требуют мокирования зависимостей
  • Основа тестового покрытия

Интеграционные тесты:

  • Проверяют взаимодействие компонентов
  • Используют реальные подсистемы
  • Медленные и потенциально нестабильные
  • Критически важны для ключевых сценариев

Правильный баланс между этими типами тестов - залог:

  • Надежности системы
  • Быстрой обратной связи при разработке
  • Уверенности в рефакторинге
  • Стабильности production-окружения