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

Тестируешь ли собственный код

2.0 Middle🔥 191 комментариев
#Основы Java

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

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

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

Тестирование собственного кода

Да, абсолютно. Тестирование — это не отдельная стадия, а неотъемлемая часть разработки. За 10+ лет я понял, что код без тестов — это код, который сломается в самый неожиданный момент в продакшене.

Почему я тестирую

1. Уверенность в коде

Тесты — это страховка. Они дают уверенность, что:

  • Код работает так, как я его спроектировал
  • Рефакторинг не сломает существующий функционал
  • Новые разработчики не испортят мою логику

2. Экономия времени

На первый взгляд, тесты требуют времени. Но на практике:

  • Без тестов: ловишь баги в продакшене, срочно ищешь причину, переделываешь
  • С тестами: баги выявляются сразу, исправляются на месте

3. Документация кода

Хорошие тесты — это живая документация. Из теста видно:

  • Что должен делать метод
  • Какие граничные случаи он обрабатывает
  • Какой результат ожидается

Стратегия тестирования

Я использую пирамиду тестирования:

  • Unit тесты (много, быстро)
  • Integration тесты (среднее количество)
  • E2E тесты (редко, долго)

Unit тесты (80% времени)

Тесты отдельных методов, изолированные от зависимостей:

public class OrderCalculatorTest {
    private OrderCalculator calculator;
    
    @BeforeEach
    void setUp() {
        calculator = new OrderCalculator();
    }
    
    @Test
    void testCalculateSubtotal_ValidItems() {
        OrderItem item1 = new OrderItem("SKU1", 100.0, 2);
        OrderItem item2 = new OrderItem("SKU2", 50.0, 1);
        Order order = new Order(Arrays.asList(item1, item2));
        
        double subtotal = calculator.calculateSubtotal(order);
        
        assertEquals(250.0, subtotal);
    }
    
    @Test
    void testCalculateSubtotal_WithDiscount() {
        OrderItem item = new OrderItem("SKU1", 100.0, 1, true);
        Order order = new Order(Arrays.asList(item));
        
        double subtotal = calculator.calculateSubtotal(order);
        
        assertEquals(90.0, subtotal);
    }
    
    @Test
    void testCalculateSubtotal_EmptyOrder() {
        Order order = new Order(Collections.emptyList());
        
        double subtotal = calculator.calculateSubtotal(order);
        
        assertEquals(0.0, subtotal);
    }
}

Integration тесты (15% времени)

Тесты взаимодействия компонентов:

@SpringBootTest
class OrderProcessorIntegrationTest {
    @Autowired
    private OrderProcessor orderProcessor;
    
    @Autowired
    private OrderRepository orderRepository;
    
    @MockBean
    private EmailService emailService;
    
    @Test
    void testProcessOrder_SavesAndSendsEmail() {
        Order order = new Order(Arrays.asList(
            new OrderItem("SKU1", 100.0, 1)
        ));
        
        orderProcessor.processOrder(order);
        
        Order saved = orderRepository.findById(order.getId()).orElseThrow();
        assertEquals("PROCESSED", saved.getStatus());
        verify(emailService).sendConfirmation(eq(order), any());
    }
}

E2E тесты (5% времени)

Тесты всей системы через API:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderApiE2ETest {
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Test
    void testCreateOrderEndToEnd() {
        OrderRequest request = new OrderRequest(Arrays.asList(
            new OrderItemRequest("SKU1", 100.0, 1)
        ));
        
        ResponseEntity<OrderResponse> response = restTemplate.postForEntity(
            "/api/orders",
            request,
            OrderResponse.class
        );
        
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertNotNull(response.getBody().getId());
    }
}

Инструменты, которые я использую

JUnit 5

Стандартный фреймворк для unit тестов.

Mockito

Для изоляции зависимостей:

@Mock
private UserRepository repository;

@InjectMocks
private UserService service;

@Test
void testGetUser() {
    User user = new User(1L, "John");
    when(repository.findById(1L)).thenReturn(Optional.of(user));
    
    User result = service.getUser(1L);
    
    assertEquals("John", result.getName());
    verify(repository).findById(1L);
}

AssertJ

Читаемые проверки:

assertThat(list)
    .hasSize(3)
    .contains("A", "B", "C")
    .doesNotContainNull();

TestContainers

Для интеграционных тестов с реальной БД в Docker:

@Container
static PostgreSQLContainer<?> postgres = 
    new PostgreSQLContainer("postgres:14")
        .withDatabaseName("test");

JaCoCo

Для измерения покрытия кода:

mvn clean test jacoco:report

Цель: минимум 80% покрытие, для критичного кода — 95%+.

Подход: TDD (Test-Driven Development)

Очень часто я использую TDD:

  1. RED → Написать тест (он падает)
  2. GREEN → Написать минимальный код (тест проходит)
  3. REFACTOR → Улучшить код (тесты остаются зелёными)

Пример:

@Test
void testCalculatePriceWithTax() {
    double price = calculator.calculateWithTax(100, "USA");
    assertEquals(108, price);
}

public double calculateWithTax(double price, String country) {
    double taxRate = getTaxRate(country);
    return price * (1 + taxRate);
}

private double getTaxRate(String country) {
    return switch(country) {
        case "USA" -> 0.08;
        case "UK" -> 0.20;
        default -> 0.0;
    };
}

Граничные случаи, которые я тестирую

  • Null значения: null, пустые коллекции
  • Граничные значения: минимум, максимум, ноль, отрицательные числа
  • Исключения: какие ошибки должен выбросить метод
  • Состояние: объект до и после операции
  • Параллелизм: race conditions, потокобезопасность
@Test
void testWithNullInput() {
    assertThrows(IllegalArgumentException.class, 
        () -> calculator.calculate(null));
}

@Test
void testWithZeroValue() {
    assertEquals(0, calculator.calculate(0));
}

Мой подход к тестированию в команде

  1. Обязательные тесты: каждый PR должен иметь тесты
  2. Code review на тесты: проверяю качество тестов
  3. CI/CD: тесты запускаются автоматически перед мерджем
  4. Метрики: отслеживаю покрытие, скорость выполнения

Вывод

Тестирование — это не роскошь, а необходимость. Чем раньше я пишу тесты, тем:

  • Меньше багов в продакшене
  • Быстрее я разрабатываю
  • Увереннее я себя чувствую при рефакторинге
  • Проще новым разработчикам понять код

Тесты — это инвестиция в качество, которая окупается сотни раз.

Тестируешь ли собственный код | PrepBro