Ответ
На последнем проекте мы использовали многоуровневую стратегию тестирования, интегрированную в CI/CD-пайплайн.
1. Модульные тесты (Unit Tests):
- Инструменты: xUnit (предпочтительнее) или NUnit + Moq для мокинга зависимостей.
- Цель: Проверка логики отдельных классов и методов в изоляции.
-
Пример:
[Fact] public void CalculateDiscount_ForPremiumCustomer_Returns20Percent() { // Arrange var customer = new Customer { IsPremium = true }; var calculator = new DiscountCalculator(); // Act var result = calculator.CalculateDiscount(customer, 1000); // Assert Assert.Equal(200, result); // 20% от 1000 } - Покрытие: Стремились к >80% покрытия кода (измерялось через Coverlet/ReportGenerator).
2. Интеграционные тесты (Integration Tests):
- Инструменты: xUnit +
WebApplicationFactory<T>(для тестирования API) + Testcontainers для поднятия реальной БД в Docker. - Цель: Проверка взаимодействия нескольких компонентов (например, контроллер -> сервис -> база данных).
- Ключевой момент: Тестовая база пересоздавалась для каждого прогона тестов, чтобы обеспечить изоляцию.
3. Сквозные тесты (End-to-End / E2E Tests):
- Инструменты: Playwright (для веб-интерфейса) и Postman/Newman (для API).
- Цель: Имитация действий реального пользователя в среде, максимально близкой к продакшену (Staging).
- Запуск: Выполнялись реже (ночью или перед релизом), так как были медленными и хрупкими.
4. CI/CD-интеграция:
- GitHub Actions / Azure DevOps: При каждом пулл-реквесте автоматически запускались модульные и быстрые интеграционные тесты.
- Порог: Если тесты не проходили или падало покрытие — сборка (build) помечалась как неудачная, что блокировало мерж.
- Артефакты: Результаты тестов и отчет о покрытии публиковались в виде артефактов пайплайна.
Дополнительные практики:
- Тестовая пирамида: Много модульных тестов, меньше интеграционных, совсем мало E2E.
- Тест-дизайн: Использование паттернов Given-When-Then для читаемости.
- Моки vs. Стабы: Четкое разделение: Moq для проверки взаимодействия (mocks), ручные реализации для подмены данных (stubs).