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

Что покрывают тесты

2.3 Middle🔥 161 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что покрывают тесты

Тесты — это автоматические проверки, которые убеждаются в том, что код работает как ожидается. Они покрывают различные аспекты: функциональность, граничные случаи, производительность, интеграцию и надёжность.

Основные типы тестов

1. Unit тесты

// Покрывают отдельные методы/функции в изоляции

public class CalculatorTest {
    private Calculator calculator;
    
    @Before
    public void setUp() {
        calculator = new Calculator();
    }
    
    @Test
    public void testAddition() {
        // Arrange
        int a = 5, b = 3;
        
        // Act
        int result = calculator.add(a, b);
        
        // Assert
        assertEquals(8, result);
    }
    
    @Test
    public void testDivisionByZero() {
        // Покрывает граничный случай
        assertThrows(ArithmeticException.class, () -> {
            calculator.divide(10, 0);
        });
    }
}

// Покрывает:
// - Корректность бизнес-логики
// - Граничные случаи
// - Обработка ошибок

2. Integration тесты

// Покрывают взаимодействие между компонентами

@SpringBootTest
public class UserServiceIntegrationTest {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    public void testUserCreationAndRetrieval() {
        // Покрывает: сохранение в БД + получение
        User user = new User("john@example.com", "John");
        User saved = userService.createUser(user);
        
        User retrieved = userRepository.findById(saved.getId()).orElse(null);
        assertNotNull(retrieved);
        assertEquals("john@example.com", retrieved.getEmail());
    }
    
    @Test
    public void testEmailNotification() {
        // Покрывает: бизнес-логика + внешний сервис
        User user = userService.createUser(new User("test@example.com", "Test"));
        // Email должен быть отправлен
        // Проверяется взаимодействие с EmailService
    }
}

// Покрывает:
// - Интеграцию с БД
// - Интеграцию с внешними сервисами
// - Взаимодействие между слоями

3. API/Controller тесты

// Покрывают REST endpoints

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest {
    
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Test
    public void testCreateUser() {
        // Покрывает: HTTP запрос → ответ
        UserRequest request = new UserRequest("john@example.com", "John");
        
        ResponseEntity<UserResponse> response = restTemplate.postForEntity(
            "/api/users",
            request,
            UserResponse.class
        );
        
        assertEquals(HttpStatus.CREATED, response.getStatusCode());
        assertNotNull(response.getBody().getId());
    }
    
    @Test
    public void testGetUser() {
        // Покрывает: GET запрос
        ResponseEntity<UserResponse> response = restTemplate.getForEntity(
            "/api/users/1",
            UserResponse.class
        );
        
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertEquals("john@example.com", response.getBody().getEmail());
    }
    
    @Test
    public void testUserNotFound() {
        // Покрывает: обработка 404 ошибок
        ResponseEntity<String> response = restTemplate.getForEntity(
            "/api/users/99999",
            String.class
        );
        
        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
    }
}

// Покрывает:
// - HTTP status коды
// - Валидация входных данных
// - Формат ответа (JSON)
// - Обработка ошибок

4. End-to-End (E2E) тесты

// Покрывают полный пользовательский сценарий

public class UserFlowE2ETest {
    
    @Test
    public void testCompleteUserRegistrationFlow() {
        // 1. Пользователь регистрируется
        // 2. Получает email с подтверждением
        // 3. Подтверждает email
        // 4. Логинится в систему
        // 5. Создаёт профиль
        // 6. Получает доступ к защищённым ресурсам
        
        // Покрывает весь цикл жизни пользователя
    }
}

// Покрывает:
// - Реальный сценарий использования
// - Все слои приложения
// - Интеграцию со всеми сервисами

5. Performance тесты

// Покрывают производительность и нагрузку

public class PerformanceTest {
    
    @Test
    public void testLargeDataProcessing() {
        List<Integer> data = new ArrayList<>();
        for (int i = 0; i < 1_000_000; i++) {
            data.add(i);
        }
        
        long startTime = System.nanoTime();
        int result = data.stream().filter(x -> x > 500_000).sum();
        long duration = System.nanoTime() - startTime;
        
        // Проверяем, что исполнение быстрое (< 100мс)
        assertTrue(duration < 100_000_000, "Слишком медленно");
    }
    
    @Test
    public void testDatabaseQueryPerformance() {
        // Убеждаемся, что запрос БД выполняется < 500мс
        long start = System.currentTimeMillis();
        List<User> users = userRepository.findAllActive();
        long duration = System.currentTimeMillis() - start;
        
        assertTrue(duration < 500);
    }
}

// Покрывает:
// - Время исполнения
// - Использование памяти
// - Масштабируемость

Граничные случаи (Edge Cases)

// Тесты также должны покрывать граничные случаи

public class EdgeCaseTests {
    
    @Test
    public void testNullInput() {
        // Покрывает null значения
        assertThrows(NullPointerException.class, () -> {
            service.process(null);
        });
    }
    
    @Test
    public void testEmptyCollection() {
        // Покрывает пустые коллекции
        List<Integer> empty = new ArrayList<>();
        int result = calculator.sum(empty);
        assertEquals(0, result);
    }
    
    @Test
    public void testMaxValue() {
        // Покрывает максимальные значения
        int result = calculator.add(Integer.MAX_VALUE, Integer.MAX_VALUE);
        // может быть ошибка переполнения
    }
    
    @Test
    public void testNegativeValues() {
        // Покрывает отрицательные значения
        int result = calculator.multiply(-5, -3);
        assertEquals(15, result);
    }
}

Coverage (покрытие кода)

// Coverage показывает процент кода, покрытого тестами

// Хорошие практики покрытия:
// - 90%+ для production кода
// - 100% для критичных функций
// - 70%+ минимально

// Инструменты
// - JaCoCo (Java Code Coverage)
// - Cobertura
// - Emma

// Пример: для метода
public boolean isAdult(int age) {
    if (age < 0) return false;           // Граничный случай
    return age >= 18;                    // Основная логика
}

// Тесты, которые покрывают все пути:
// - isAdult(-1) -> false
// - isAdult(0) -> false
// - isAdult(17) -> false
// - isAdult(18) -> true
// - isAdult(100) -> true

Что не нужно тестировать

// Не тестируй:
// 1. Getter/Setter (если нет логики)
// 2. Конструкторы POJO (простые присваивания)
// 3. Библиотечный код (предполагаем, что работает)
// 4. Конфигурационные классы

// Тестируй:
// 1. Бизнес-логика
// 2. Граничные случаи
// 3. Интеграции
// 4. Обработка ошибок
// 5. Критичные пути

Пирамида тестов

        E2E  (10%)
       /    \
    Integration (20%)
     /        \
   Unit      (70%)

Заключение

Тесты покрывают:

  • Функциональность (работает ли код как задумано)
  • Граничные случаи (null, пустые значения, максимумы)
  • Интеграцию (взаимодействие компонентов)
  • Производительность (быстро ли работает)
  • Надёжность (обрабатывает ли ошибки)
  • Регрессию (не сломалось ли уже работающее)

Хорошее тестирование — это инвестиция, которая окупается через повышение надёжности, упрощение рефакторинга и снижение багов в production.