← Назад к вопросам
Является ли код корректным, если до и после изменения тест завершался успешно?
1.0 Junior🔥 161 комментариев
#Тестирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Корректность кода и успешные тесты
Ответ: НЕТ, зелёные тесты не гарантируют корректность кода. Это распространенное заблуждение, которое может привести к серьезным багам в production.
Почему зелёные тесты могут быть обманчивы?
1. Некорректные или неполные тесты
Тесты могут быть написаны неправильно и проверять не то, что нужно.
// Пример: неправильный тест
@Test
public void testCalculateDiscount() {
PricingService service = new PricingService();
double result = service.calculateDiscount(100);
// Плохой тест — не проверяет конкретное значение
assertTrue(result > 0); // ❌ Пройдёт при любом положительном числе
// Правильный тест
assertEquals(10.0, result, 0.01); // ✓ Проверяет точное значение
}
2. Граничные случаи не покрыты
public class PaymentProcessor {
public boolean processPayment(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) > 0) {
// Логика обработки платежа
return true;
}
return false;
}
}
@Test
public void testPaymentProcessing() {
PaymentProcessor processor = new PaymentProcessor();
assertTrue(processor.processPayment(new BigDecimal("100.00")));
// ✓ Тест проходит
}
// Но что с граничными случаями?
// - Отрицательная сумма?
// - Ноль?
// - Очень большие числа?
// - Null значение?
// Если нет тестов для этих случаев — код может упасть в production!
3. Тесты зависят от состояния
public class UserRepository {
private static List<User> users = new ArrayList<>(); // ❌ Статический state
public void saveUser(User user) {
users.add(user);
}
public int getUserCount() {
return users.size();
}
}
@Test
public void testUserCount() {
UserRepository repo = new UserRepository();
repo.saveUser(new User("Alice"));
assertEquals(1, repo.getUserCount());
// ✓ Тест проходит в первый раз
// Но если запустить дважды подряд, второй раз users.size() = 2!
// Тесты зависят друг от друга и от порядка выполнения
}
Решение: Использовать setup/teardown для очистки состояния.
4. Race conditions и многопоточность
public class ThreadSafeCounter {
private int count = 0; // ❌ Не потокобезопасно!
public void increment() {
count++; // ❌ Race condition
}
public int getCount() {
return count;
}
}
@Test
public void testIncrement() {
ThreadSafeCounter counter = new ThreadSafeCounter();
for (int i = 0; i < 100; i++) {
counter.increment();
}
assertEquals(100, counter.getCount());
// ✓ Может пройти (если не будет race condition)
}
@Test
public void testConcurrentIncrement() throws InterruptedException {
ThreadSafeCounter counter = new ThreadSafeCounter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
// ❌ Может быть 1952 вместо 2000 из-за race condition!
assertEquals(2000, counter.getCount());
}
5. Моки скрывают реальные проблемы
public class UserService {
private DatabaseConnection db;
public UserService(DatabaseConnection db) {
this.db = db;
}
public User findUserById(int id) {
return db.query("SELECT * FROM users WHERE id = " + id);
// ❌ SQL injection!
}
}
@Test
public void testFindUser() {
// Используем mock вместо реальной БД
DatabaseConnection mockDb = mock(DatabaseConnection.class);
User user = new User(1, "Alice");
when(mockDb.query(any())).thenReturn(user);
UserService service = new UserService(mockDb);
User result = service.findUserById(1);
assertEquals("Alice", result.getName());
// ✓ Тест проходит, но SQL injection не обнаружена!
}
// Правильное решение: использовать параметризованные запросы
public User findUserById(int id) {
return db.query("SELECT * FROM users WHERE id = ?", id);
// ✓ Защищено от SQL injection
}
6. Производительность и deadlock
public class DeadlockExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void method1() {
synchronized(lock1) {
Thread.sleep(10);
synchronized(lock2) {
// Операция
}
}
}
public void method2() {
synchronized(lock2) { // Обратный порядок блокировок!
Thread.sleep(10);
synchronized(lock1) {
// Операция
}
}
}
}
// Тесты могут пройти, но при определённой синхронизации
// потоков произойдет deadlock в production!
7. Внешние зависимости
public class PaymentGatewayClient {
public boolean processPayment(String cardToken, BigDecimal amount) {
// Вызов реального API платежной системы
HttpResponse response = httpClient.post("https://payment-api.com/charge",
cardToken, amount);
return response.isSuccess();
}
}
@Test
public void testPayment() {
PaymentGatewayClient client = new PaymentGatewayClient();
assertTrue(client.processPayment("tok_valid", new BigDecimal("100")));
// ✓ Пройдёт, если API доступна
// ❌ Но что если API недоступна или вернула ошибку в production?
}
// Нужно мокировать внешние зависимости
Признаки того, что тесты неполные
public class QualityMetrics {
// 1. Низкое покрытие кода (coverage < 80%)
// 2. Нет тестов для исключений
// 3. Нет интеграционных тестов
// 4. Тесты очень быстрые (может быть скрывают реальные проблемы)
// 5. Нет тестов для граничных случаев
// 6. Нет тестов для многопоточности
// 7. Нет performance тестов
// 8. Нет security тестов
}
Правильный подход
public class ComprehensiveTestingStrategy {
/*
1. Unit тесты:
- Все логические ветки
- Граничные случаи
- Исключения
2. Интеграционные тесты:
- Реальная БД (или тестовая)
- Реальные API (или VCR/mock servers)
3. E2E тесты:
- Полный flow пользователя
4.性能 тесты:
- Load testing
- Stress testing
5. Security тесты:
- SQL injection
- XSS
- CSRF
- Authentication/Authorization
*/
}
Цитата из индустрии
"Тесты показывают отсутствие ошибок только при условиях, которые вы тестировали. Они не могут доказать полную корректность." — Dijkstra
Итог
Зелёные тесты — это необходимое условие, но не достаточное. Код может быть:
- Функционально правильным в протестированных сценариях
- Содержать баги в непокрытых случаях
- Уязвим к security атакам
- Падать под нагрузкой
- Иметь race conditions
Корректность достигается через:
- Полное покрытие тестами (>90%)
- Code review от опытных разработчиков
- Статический анализ кода
- Интеграционное тестирование
- Production мониторинг
- Security аудит
- Load тестирование