Как протестировать часть метода, не вынося её в отдельный метод?

«Как протестировать часть метода, не вынося её в отдельный метод?» — вопрос из категории Тестирование, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, можно, но это считается антипаттерном. Тестирование части метода напрямую нарушает принцип инкапсуляции и усложняет поддержку тестов.

Правильные подходы:

  1. Рефакторинг: Выделите тестируемую логику в отдельный (публичный или package-private) метод и тестируйте его.
  2. Тестирование через публичный контракт: Протестируйте всю логику метода через его публичный API, используя различные входные данные для покрытия всех веток.
  3. Использование наследования (редко): Сделать метод защищённым (protected) и переопределить его в тестовом классе для проверки.

Пример с рефакторингом (предпочтительно):

public class OrderProcessor {
    public double calculateTotal(Order order) {
        double subtotal = calculateSubtotal(order.getItems()); // Выделенная логика
        return applyTax(subtotal, order.getTaxRate());
    }
    // Теперь эту часть можно протестировать отдельно
    public double calculateSubtotal(List<Item> items) {
        return items.stream().mapToDouble(Item::getPrice).sum();
    }
}

// В тестах
@Test
void calculateSubtotal_ReturnsCorrectSum() {
    OrderProcessor processor = new OrderProcessor();
    List<Item> items = List.of(new Item(10.0), new Item(20.0));
    assertEquals(30.0, processor.calculateSubtotal(items));
}

Почему не стоит тестировать приватные методы через рефлексию: Это делает тесты хрупкими, зависимыми от внутренней реализации, и они ломаются при любом рефакторинге названия или сигнатуры метода. Фокус должен быть на тестировании наблюдаемого поведения класса.