Больше 40 тестов, 100% покрытия строк и зелёный CI — звучит как повод спокойно жать Merge. Но именно здесь ИИ и автотесты часто подсовывают команде красивую иллюзию качества: пайплайн зелёный, а баг всё равно доезжает до прода. Для русскоязычных разработчиков и тимлидов это неприятная, но полезная мысль: генерация тестов стала дешёвой, а вот цена ложной уверенности никуда не делась.
На этот сюжет обратил внимание Сергей Прощаев, Tech Lead и руководитель направления Java/Kotlin-разработки в FinTech и e-commerce: в колонке для OTUS, как пишет Habr / Карьера, он разбирает типичный кейс с сервисом скидок на Java и JUnit. Условия у задачи нарочно простые. Если сумма заказа больше 100, применяется скидка 10%. Если товаров в корзине больше пяти, действует скидка 5%. Скидки не складываются: нужно взять только большую. Дальше в игру вступает ИИ-агент, который генерирует для метода несколько десятков тестов, после чего покрытие показывает 100%, а сборка проходит без единого красного флага.
Проблема в том, что зелёный статус сам по себе не отвечает на главный вопрос: может ли конкретный тест уронить сборку, если бизнес-логика сломана. В этом и есть нерв всей истории. Один из приведённых примеров проверяет лишь, что результат не равен null. Формально это тест, practically это декоративная штукатурка. Если метод вернёт исходную сумму без скидки, ноль, любую другую неправильную цифру, такой тест всё равно промолчит. В экосистеме, где LLM охотно штампуют десятки похожих методов, именно такие «проверки на наличие пульса» быстро надувают покрытие и создают ощущение, что код под защитой. На ревью они выглядят солидно: аннотации, вызов метода, ассерт. По сути они проверяют лишь то, что JVM не взорвалась.
Второй важный тип ловушки тоньше и опаснее, потому что выглядит уже вполне прилично. Тест вычисляет ожидаемое значение для заказа на 200 единиц с тремя товарами и сравнивает его с результатом метода. На первый взгляд всё правильно: есть входные данные, есть конкретное expected, есть assertEquals. Но здесь легко прячется классическая ошибка тестов, сгенерированных по коду, а не по требованиям: expected собирается по той же логике, которую тест и должен независимо проверять. Если тест повторяет внутреннюю формулу production-кода, а не опирается на отдельное бизнес-ожидание, он перестаёт быть хорошим оракулом. Да, такой сценарий полезнее пустого assertNotNull, но он всё равно не закрывает вопрос качества набора тестов целиком. Он говорит: «на одном счастливом пути число сошлось». Он не говорит: «логика защищена от ошибок на границах и в конфликтующих правилах».
И вот тут статья попадает в самый практичный нерв нынешнего тренда. ИИ и автотесты уже неплохо справляются с механикой: развернуть каркас, набросать сценарии, дотащить покрытие, иногда даже аккуратно оформить названия кейсов. Но модель не отвечает за смысл теста автоматически. Она легко делает то, что люди тоже делали годами, только теперь быстрее: плодит шум, копирует дефектные предположения и закрепляет неверные требования в зелёном виде. В примере со скидками это видно особенно хорошо на тесте границы для суммы 100 и на заведомо ошибочном сценарии, где ожидается суммирование скидок до 15%, хотя правило сервиса прямо запрещает складывать их между собой. Такой тест не просто бесполезен. Он способен легализовать баг и заставить команду чинить код «под тест», а не под бизнес-логику.
Для разработчиков отсюда следует довольно жёсткий, но здоровый вывод: качество тестов по-прежнему определяется не количеством методов и не процентом покрытия, а силой ассертов и независимостью ожидаемого результата. Для тимлидов и техдиров вывод ещё практичнее. Если в команде уже используют агентов для генерации тестов, нужно ревьюить не только production-код, но и саму тестовую стратегию: есть ли проверки на границы, есть ли негативные сценарии, не дублирует ли expected реализацию, не зашиты ли в тесты неверные требования. Иначе компания получает ускорение в написании тестов без ускорения в обнаружении дефектов. Для HR и менеджеров по найму в этом тоже есть сигнал: умение работать с LLM в тестировании теперь мало похоже на «умеет нажать Generate tests». Гораздо важнее навык быстро отделить тест, который ловит баг, от теста, который просто красиво светится зелёным.
Рынок в целом движется именно в эту сторону. Генерация кода и тестов становится массовой и почти бытовой функцией, а инженерная ценность смещается в слой проверки допущений. Несколько лет назад спорили о том, нужен ли вообще высокий процент покрытия. Теперь спор тоньше: что именно скрывается внутри этих процентов и насколько тестовый набор способен пережить поломку реальной логики. Пожалуй, ближайший фильтр зрелости для команд будет совсем не в том, кто быстрее подключил ИИ-агента к репозиторию, а в том, кто научился не верить первой зелёной галочке.