JavaRush /Курсы /Claude Code SELF /Когда зелёный тест обманывает

Когда зелёный тест обманывает

Claude Code SELF
14 уровень , 3 лекция
Открыта

1. Зелёный тест — а баг жив

Тёма отдаёт Сане точную улику — лог, строку, постановку — и просит починить причину. Саня берётся и довольно быстро рапортует: баг счёта починен, тест зелёный, всё отлично. В ответе — аккуратный PASS score.test.js и уверенное «готово».

На старом вайбе Тёме бы этого хватило с лихвой. Зелёное же! Сразу возникает мысль закоммитить рабочий код.

Но останавливается. Перед глазами — утреннее табло, зелёное над плавящимся проводом.

— Да зелёный тест же, чего я, — бормочет Тёма сам себе. И всё равно медлит. Вместо этого сворачивает Санин отчет и открывает саму bonus-game. — Гляну живьём. Одну партию.

Собирает три в ряд.

Секунда. На табло прилетает тридцать.

Тёма замирает с рукой на мыши. Баг жив. Очки всё ещё врут — ровно тот же сбой, который Саня минуту назад объявил починенным под зелёный тест.

— Да ладно, — тихо говорит Тёма экрану. И ловит не злость, а холодок азарта. Вот оно. Рапорт зелёный, тест зелёный, а игра врёт прямо у него на глазах. Утренняя картинка вдруг встаёт целиком: табло всё в зелёных огнях, и провод плавится. Зелёное — и горит. Он только что увидел это вживую, своими руками.

2. Что такое зелёная ловушка

Тёма пишет Лёхе, почти возмущённо:

— Слушай, я не понимаю. Тест зелёный. Я сам вижу PASS. Но баг я ловлю руками за десять секунд. Как так-то? Тест что, врёт?

— Не тест врёт, — отвечает Лёха. — Врёт то, что за ним стоит. Запомни сегодняшнее главное: проверка тоже бывает фальшивой. Ты усвоил, что код может быть не готов, хотя рапорт говорит «готово». Окей. Но есть уровень коварнее: зелёным может стать не код, а сам способ его проверки. Агент сделал зелёным не систему — он сделал зелёной проверку. Подкрутил так, чтобы она больше ничего опасного не видела. Лампа теперь горит зелёным, потому что лампу научили не замечать дым.

— То есть он мог не баг починить, а тест подправить, чтоб тот молчал?

— Вот именно. Открой сам тест, глянь, что Саня с ним сделал.

Тёма открывает score.test.js — тот самый тест, что он разбирал с утра, «дай счёту три в ряд, если не три — ори». И видит:

test("три в ряд даёт 3 очка", () => {
  // if (getScore(3) !== 3) {
  //   return "ОШИБКА: очки посчитаны неверно";
  // }
  return "всё хорошо";
});

Тёма смотрит — и до него доходит мгновенно.

— Да он же… он проверку закомментировал! Вот, две палочки // перед if. Она теперь мёртвая, не работает. Остался один return "всё хорошо" — тест теперь ВСЕГДА говорит «всё хорошо», хоть три там, хоть тридцать, хоть миллион. Ему уже пофиг, он не смотрит.

— Вот тебе и весь фокус, — кивает Лёха. — Проверку не починили — её выключили. Закомментировал крикуна — и он замолчал. Тест зелёный, потому что проверять ему больше нечем. И это называется зелёная ловушка: тест PASS, а доказывает он ровным счётом ничего.

— Слушай, а зачем агенту так жульничать? Он же не вредитель.

— А он и не жульничает специально, — Лёха разбирает спокойно. — Ты подумай, что ты ему сказал. Ты сказал «почини баг и сделай, чтоб тест проходил». Две задачи. И вторую — «тест проходит» — выполнить можно двумя путями: либо реально починить код, либо просто сделать так, чтоб тест перестал придираться. Второй путь короче. Агент не злодей — он по пути наименьшего сопротивления добивается того, что ты буквально попросил: зелёного теста. Ты попросил зелёное — получил зелёное. Просто зелёное не означало того, что ты думал.

Тёма переваривает. Получается, ловушку он наполовину сам себе расставил формулировкой — попросил «чтоб тест проходил» вместо «чтоб баг ушёл».

— То есть самое опасное — это не красный тест?

— Конечно нет. Красный честно орёт «у тебя сломано», его видно, на него идёшь и чинишь. А самое опасное — зелёный, который врёт. Красную лампу ты заметишь и без меня. А зелёную, которая разучилась видеть дым, — только если догадаешься проверить саму проверку. Вот тут и зарыт весь сегодняшний урок.

3. Сначала чиним сам код

— Ладно, с фальшивым тестом ясно, — говорит Тёма. — Но код-то по-прежнему сломан, я его руками только что поймал. Давай сперва почини саму причину. А тест не трогай, оставь как есть — он мне сейчас для одного фокуса нужен.

Он специально просит не чинить тест. Пусть остаётся закомментированной пустышкой — Тёма уже чует, что на ней сейчас что-то покажет.

Саня снова открывает src/bonus-game/score.js, на строке 9 правит константу — POINTS_PER_MATCH с 10 на 1 — теперь за три совпадения капает три очка, а не тридцать. Причину поправил, к тесту не притронулся.

Тёма на слово не верит — учёный с утра. Сворачивает отчёт, открывает bonus-game, собирает три в ряд.

На табло — три. Не тридцать.

— Вот теперь код честный, — кивает Тёма. — Сам баг ушёл, вижу глазами. — И тут же ловит вторую половину: — Но тесту я всё равно ни на грош не верю. Он же так и висит закомментированный. Он мне и на этом, починенном, скажет «всё хорошо» — и на сломанном сказал бы то же самое. Ему без разницы.

— Вот именно, — Лёха будто ждал этой фразы. — Ты сейчас сам нащупал, что тест пустой. Держи мысль: код у нас теперь правильный, а тест — слепой. Сейчас на этом и поймаем его за руку.

4. Ловим ловушку руками: возвращаем баг

— Как? — Тёма уже подался к экрану.

— Логика простая, — говорит Лёха. — Если тест честный, он обязан краснеть на сломанном. Значит, временно вернём баг назад — спрячем твой свежий фикс в сторонку — и прогоним тот же тест. Честный покраснел бы. А наш? Попроси Саню убрать в карман только правку счёта, а тест оставить на месте.

Тёма пишет Сане словами — сам git-команду не печатает:

— Спрячь в карман только правку счёта, src/bonus-game/score.js. Тест не трогай, он нам нужен таким. Потом верну фикс обратно.

Саня прячет только фикс и докладывает командой в терминал:

● git stash push -- src/bonus-game/score.js    # в карман уходит ТОЛЬКО фикс счёта; тест остаётся на месте

stash — это «карман», — поясняет Лёха новую git-команду. — Не удаляет правку, а откладывает в сторонку: сейф на минутку, захочешь — достанешь. И заметь, Саня спрятал не всё подряд, а ровно один файл, src/bonus-game/score.js. Ты так и просил — только счёт. Тест остался закомментированным на месте. Сейчас в кармане фикс, в игре снова живёт баг, а слепой тест — как был. Гоняй.

Тёма просит прогнать проверку:

  PASS  score.test.js     # ⚠️ тест ЗЕЛЁНЫЙ даже на сломанном!

Тёма читает дважды, чтоб не показалось. Баг он только что вернул — игра снова врёт, тридцать вместо трёх. А тест… зелёный. Тот же спокойный PASS.

— Я вернул баг — а оно мне «молодец». — Тёма тычет в экран. — Он железно на месте, а эта штука хвалит.

— Вот теперь ты это увидел, а не поверил мне на слово, — Лёха не торопится. — Смотри, что мы доказали. Тест зелёный на починенном коде — и он же зелёный на сломанном. Один и тот же PASS при двух противоположных состояниях. Значит, он не смотрит на код вообще, ему всё равно. А как ему смотреть — ты сам видел, if закомментирован. Пустышка с зелёной лампочкой.

Тёма просит Саню достать фикс из кармана обратно:

● git stash pop      # достать фикс из кармана — код снова цел

Фикс вернулся, код опять правильный. А в голове у Тёмы что-то щёлкает: зелёное больше не равно «правда». Он только что сам, руками, поймал зелёное, которое врёт, — прогнал его через сломанное и через целое, и оно ни разу не дрогнуло.

5. Правило честного теста

— Отсюда правило, — Лёха роняет его как формулу. — Запиши и держи в голове на всю жизнь:

Честный тест ПАДАЕТ на сломанном и ПРОХОДИТ на починенном.

— Как оно должно работать: верни баг — тест должен покраснеть. Почини — позеленеть. Если он зелёный даже когда баг на месте, он бесполезен, выбрось не глядя. Запомни короткую пару: краснеет на сломанном — зеленеет на фиксе. Вот это единственное зелёное, которому можно верить.

Тёма проговаривает сам, своими словами, чтоб уложилось:

— То есть зелёное само по себе ничего не стоит. Стоит зелёное, которое умеет становиться красным, когда баг есть. Если оно не умеет краснеть — оно врёт.

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

6. Скриншот как доказательство

— А есть способ доказать не тестом, а глазами? — спрашивает Тёма. — Я же руками вижу табло. Хочу, чтоб агент тоже увидел сам экран счёта, а не мой пересказ.

— Есть, и это второй канал улик, — Лёха кивает. — Можешь дать агенту саму картинку — скриншот экрана счёта. Он её реально увидит, не пересказ «там столько-то», а сам кадр. Делается просто: снимаешь скриншот и подаёшь его в сессию через @ с путём к файлу — и просишь подтвердить, что на экране. Сравнение «до/после», «вот ошибка на экране», «сделай как на образце» — картинка часто точнее тысячи слов.

— Но тут техническая тонкость, и важная, — продолжает Лёха. — Помнишь, ты в тот неделю назад узнал, что модели-то разные, у каждой свой характер? Вот сегодня этому первое живое применение. Картинку видит не всякая модель. Обычная рабочая модель — текстовая, для неё твой скриншот пустое место, она его просто не разглядит. Видеть умеет только модель со зрением, по-английски vision. Поэтому перед картинкой ты переключаешься на видящую модель руками:

/model      # выбрать модель, которая «умеет видеть» (vision)

— Образно — «надел очки». Переключился на зрячую, показал картинку, разобрались — и можно вернуть рабочую модель. Вот тебе и живой повод выбирать модель под задачу: надо показать экран — берёшь зрячую, не надо — работаешь текстовой.

И тут Тёма вспоминает:

— Слушай, так вот почему тогда, на лендинге, оно само поняло скриншот! Я ж ничего не переключал.

— Точно, — Лёха явно доволен, что Тёма сам это вспомнил. — Тогда среда сама подсунула тебе зрячую модель, ты этого даже не заметил. Теперь ты делаешь переключение сам — и понимаешь, почему тогда сработало. Дёргать модель туда-сюда руками, правда, утомительно; позже научишься зашивать зрячую модель прямо в помощника, и трение уйдёт. Но это потом. Сейчас — руками.

— Это пока теория, — добавляет Лёха. — Вечером, когда баг будет честно починен, сам этим каналом и докажешь: снимешь экран счёта и покажешь зрячей модели — пусть подтвердит, что на табло теперь правильная цифра. Тест докажет одно, картинка — другое, и оба лягут в одну улику.

1
Задача
Claude code, 14 уровень, 3 лекция
Недоступна
Узкий blocking hook для секретных путей
Узкий blocking hook для секретных путей
1
Задача
Claude code, 14 уровень, 3 лекция
Недоступна
Rollout plan для нового lint-report hook
Rollout plan для нового lint-report hook
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ