Как тестировать методы с множеством ветвлений
Что проверяют
Умеете ли вы системно подобрать набор тестов, а не писать их наугад, и видите ли в куче ветвлений повод для рефакторинга. Следом часто спрашивают, что даёт 100% покрытия и чего оно не гарантирует.
Как отвечать
Сначала оцените масштаб: цикломатическая сложность по Маккейбу равна числу точек решения плюс один и задаёт минимум независимых путей. Если в методе десяток if, да ещё внутри циклов, это уже сигнал к упрощению.
Как подбирать тесты:
- классы эквивалентности и граничные значения: на каждое условие по представителю класса и значения на границе,
0,-1, пустая строка,null; - таблица решений: для сложных сочетаний условий, чтобы не пропустить комбинацию и не плодить лишние;
- параметризация: JUnit 5
@ParameterizedTestс@CsvSourceили@MethodSource, по строке на ветку и с понятнымname, чтобы в отчёте было видно, какая ветка упала; - исключения:
assertThrowsиз JUnit 5 илиassertThatThrownByиз AssertJ, а не@Test(expected = ...)из JUnit 4, который не проверяет, где именно вылетело исключение; - зависимости: внешние вызовы подменяются Mockito, чтобы ветки переключались входными данными, а не состоянием базы.
Покрытие меряйте JaCoCo по веткам, а не по строкам: частично покрытое условие он подсвечивает жёлтым. Качество самих тестов проверяет мутационное тестирование PIT: оно меняет > на >= и смотрит, упадёт ли хоть один тест.
Сильный финал ответа: если веток слишком много, метод стоит разбить. Помогают выделение методов, стратегия или полиморфизм, а в Java 21 switch с сопоставлением по образцу над запечатанной (sealed) иерархией, где компилятор сам проверит, что все варианты обработаны.
Где спотыкаются
Гонятся за 100% покрытия строк. Строка с if (a && b) покрыта одним тестом, хотя половина комбинаций не проверена.
Пишут один большой тест с десятком проверок. Первое же падение скрывает остальные, а по названию не понять, что сломалось.
Тестируют приватные методы через рефлексию. Ветки проверяются через публичный контракт, а если это неудобно, логику пора вынести в отдельный класс.