Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Тестирование: TDD vs Unit Tests
Это важный вопрос о методологии разработки. Дам честный ответ, основанный на опыте.
Идеальный подход: Test-Driven Development (TDD)
Теория гласит: пиши тесты ДО написания кода. Процесс:
- RED - напиши падающий тест
- GREEN - напиши минимальный код для прохождения
- REFACTOR - улучши без изменения поведения
@Test
public void shouldCalculateSum() {
Calculator calc = new Calculator();
int result = calc.add(2, 3);
assertEquals(5, result);
}
// Сначала тест падает, потом пишешь:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
Преимущества TDD:
- Гарантированное покрытие - весь код имеет тесты
- Лучший дизайн - API проектируется с позиции пользователя
- Меньше багов - проблемы выявляются сразу
- Уверенность в рефакторинге - тесты защищают от регрессии
- Живая документация - тесты показывают как использовать код
Реальная практика: гибридный подход
В production-коде часто используют комбинацию:
1. TDD для сложной бизнес-логики
// Сложная логика скоринга - пишу тест ДО кода
@Test
public void shouldCalculateScoreCorrectlyWithBonus() {
ScoreCalculator calc = new ScoreCalculator();
int score = calc.calculate(100, true); // bonus
assertEquals(150, score);
}
public class ScoreCalculator {
public int calculate(int baseScore, boolean hasBonus) {
return hasBonus ? baseScore + 50 : baseScore;
}
}
2. Unit-тесты после для стандартного кода
// Простой CRUD метод - сначала пиши код
public User getUserById(long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
// Потом тест
@Test
public void shouldThrowExceptionWhenUserNotFound() {
assertThrows(UserNotFoundException.class, () -> {
service.getUserById(999);
});
}
Мой подход (10+ лет опыта)
Я практикую TDD для критичного функционала и Unit tests ПОСЛЕ для стандартного кода:
- TDD: платежные системы, расчёты, алгоритмы
- Tests after: CRUD, маппинг, просто getters/setters
Критерий выбора:
- Если логика может иметь edge cases → TDD
- Если логика простая и понятная → тест после
Когда тесты ОБЯЗАТЕЛЬНЫ (даже простые)
// ❌ Никогда не оставляй без тестов:
// 1. Публичное API
public List<User> findByRole(UserRole role) { ... }
// 2. Бизнес-логика
public BigDecimal calculateDiscount(Customer c) { ... }
// 3. Обработка ошибок
public void validateEmail(String email) { ... }
// 4. Многопоточный код
public synchronized void updateCache() { ... }
Хорошая практика: покрытие тестами
// Структура проекта
src/main/java/
└─ com/example/
├─ domain/
├─ service/
└─ controller/
src/test/java/
└─ com/example/
├─ domain/ // 95%+ покрытие (критично)
├─ service/ // 90%+ покрытие
└─ controller/ // 70%+ покрытие (много мокируется)
Минус "тестов после"
// Когда пишешь код БЕЗ предварительных тестов,
// часто упускаешь edge cases:
public int divide(int a, int b) {
return a / b; // Забыл проверить b == 0!
}
// С TDD эта ошибка была бы выявлена сразу:
@Test
public void shouldThrowExceptionOnDivideByZero() {
assertThrows(IllegalArgumentException.class, () -> {
calc.divide(10, 0);
});
}
Инструменты и метрики
# JaCoCo для отчётов покрытия
./gradlew jacocoTestReport
# Минимум 80-90% для production
<limit>
<element>LINE</element>
<includes>
<include>*</include>
</includes>
<excludes>
<exclude>*Test</exclude>
</excludes>
<minimum>0.85</minimum>
</limit>
Мой совет
- Всегда пиши тесты (или до, или после)
- TDD для сложной логики - это просто мудро
- Не занимайся религиозными войнами - обе стороны работают
- Главное - покрытие есть, а не когда оно написано
- Code review должен требовать тестов - это ответственность команды
Заключение
Практикую комбинированный подход:
- TDD для сложных алгоритмов и бизнес-логики
- Unit tests after для стандартного CRUD кода
- Всегда ≥90% покрытие для critical path
- CI/CD проверяет покрытие - непройти нельзя
Идеального ответа нет - выбирайте подход под вашу команду и проект.