PunchVIDEO: From Schmeichel to Alisson, six goalkeepers with EPL goalsThe Jerusalem PostANU adds three new artworks to 'October 7' exhibit as Israel marks third anniversary of massacreESPNTransfer rumors, news: Bayern brace for tough contract talks with OliseBollywood HungamaRakul Preet Singh BREAKS silence on Income Tax searches, DENIES involvement in illegal foreign remittances: "Have been paying my due taxes since the age of 20"InquirerFoul odor prompts shoreline inspection in Mamburao한겨레[인터뷰] 미 공화당 텃밭서 11년 버틴 한국계 정치인…이제 주지사 노린다ZDF heuteAktuelle Pressemitteilungen des ZDFSBS 뉴스군 "북한군 지뢰 최종 판단, 오늘 지뢰 지대 제거 작전"SportstarNetherlands coach Xavi satisfied with first four games in chargeVanguardRivers: Police rescue kidnap victim, nab two suspectsInvesting.comGoldman Sachs anticipe des gains pour le real brésilien après les électionsInvesting.comGoldman Sachs prevé ganancias del real brasileño tras elecciones
The Daily Newsstand · Free, Always
Monday, October 5, 2026

Тест проходит даже с багом: что показал эксперимент Дэна Лу

Translate

Пусть функция должна разворачивать строку. Агент написал тест: на входе abba, на выходе ожидается abba. Тест зеленый. Он останется зеленым, если функция вообще ничего не делает.

Пример учебный, но ошибка выбора входа реальная: в одном из прогонов эксперимента Дэна Лу агент проверял разворот битового потока на палиндроме. Назвал рискованное место, независимо вывел результат, провел аудит - и все равно не обнаружил ошибку. Проверка была, различия между правильным и неправильным поведением в ней не было.

Разберу три таких механизма из эксперимента, а затем покажу на коротком исполнимом примере, как проверить чувствительность теста к конкретному багу.

Что именно проверял Дэн

Агенты писали на Rust декодер Zstd по RFC. Работали в контейнере без интернета, скрытых тестов не видели. Успехом считался прогон, прошедший все скрытые тесты.

Дэн сравнил 26 условий, включая контроль без дополнительных инструкций. В основном сравнении использовал Codex с GPT-5.6 Sol на уровнях medium и xhigh, по 80 прогонов на сочетание условия и уровня. В промт добавляли указания применять TDD, фаззинг, формальные методы, аудит и другие техники; в отдельных условиях подключали скиллы.

Убедительного общего выигрыша от этих указаний не получилось, многие условия выглядели хуже контроля. Автор предупреждает: разброс велик, порядок условий в таблице не стоит принимать за надежный рейтинг методов. Кроме того, агент мог прочитать название техники и выполнить ее лишь частично. Это эксперимент о поведении агентов при таких инструкциях на конкретной задаче, а не доказательство бесполезности TDD или фаззинга.

Для практики мне интереснее подробности: что делал агент, когда проверка выглядела содержательной, но пропускала ошибку.

1. Вход скрывает различие

В Zstd есть режим с четырьмя потоками Хаффмана. Если реализация перепутала их порядок, тест должен это заметить. Но агенты использовали четыре одинаковых потока. Перестановка одинаковых частей ничего не меняет: правильная и неправильная реализации получают одинаковый результат.

Палиндром при проверке разворота - тот же механизм. Вход симметричен относительно ошибки, которую нужно обнаружить.

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

Для перестановки возьмите разные элементы. Для направления - непалиндром. Для границы - значения по обе стороны. При этом ожидаемый ответ должен следовать из контракта, иначе можно написать более разнообразный тест с неправильным эталоном.

2. Ожидаемый ответ повторяет ошибку реализации

В условии с дифференциальным тестированием 135 прогонов из 160 сделали что-то похожее на сравнение реализаций. Однако агенты не написали две полные реализации; частичные сравнения нередко повторяли один алгоритм вместе с ошибкой.

Сравнение двух результатов полезно, когда есть основания доверять независимости их получения. Если обе стороны одинаково неверно поняли RFC, совпадение ответов этого не обнаружит.

Есть и более короткий путь к той же ловушке: запустить код и записать его ответ в ожидаемый результат теста. После этого тест может хорошо защищать текущее поведение от изменений. Соответствует ли оно требованиям - отдельный вопрос.

Поэтому у теста стоит спрашивать, откуда взялся эталон. Это пример из спецификации, ручной расчет, результат проверенной реализации? Или ответ того самого кода, который мы сейчас проверяем?

Другой агент или свежий контекст могут помочь с независимым выводом, но сами по себе верный эталон не гарантируют. В эксперименте независимый аудит был у 42 агентов; эти прогоны показали худший результат, причем Дэн прямо допускает обратную связь: отдельный аудит могли выбирать в более трудных случаях. Из этих данных нельзя вывести ни пользу, ни вред отдельного агента как причинный эффект.

3. Проверка обходит рискованную ветку

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

В 10 прогонах из 160 агенты генерировали структурированные случайные входы; в половине этих прогонов нашли реальные баги. Это небольшая наблюдаемая группа, а не оценка универсальной эффективности приема. Но различие в поведении понятно: вход должен добраться до механизма, который мы хотим проверить.

У формальных методов встречалась похожая проблема. Агент доказывал, что при допустимом индексе обращение остается в границах, тогда как ошибка была в другом свойстве. Доказательство могло быть корректным и все равно не защищать от нужного бага.

Здесь полезно сначала назвать нарушение: перепутанный порядок потоков, потерянный перенос, неверное состояние протокола. Затем проверить, что выбранный вход и утверждение способны его обнаружить.

Как проверить сам тест

Ниже учебный пример на Python. Он иллюстрирует выбор входа, а не воспроизводит декодер из эксперимента Дэна.

def reverse_ok(text):
    return text[::-1]


def reverse_bug(text):
    return text  # намеренная ошибка: разворот потерян


def weak_test(reverse):
    assert reverse("abba") == "abba"


def useful_test(reverse):
    assert reverse("abcd") == "dcba"


def passes(test, implementation):
    try:
        test(implementation)
    except AssertionError:
        return False
    return True


for test in (weak_test, useful_test):
    print(test.__name__,
          passes(test, reverse_ok),
          passes(test, reverse_bug))

Результат:

weak_test True True
useful_test True False

Первый тест не обнаруживает выбранную ошибку. Второй проходит на правильной реализации и падает на намеренно сломанной. Мы получили конкретное свидетельство, за что отвечает проверка. Ее полноты этот пример не доказывает: пустые строки, Unicode и остальные свойства контракта требуют своих случаев.

Это простейший пример мутационного тестирования: внести контролируемое изменение в код и проверить, обнаружат ли его тесты. В обсуждении на Hacker News coder-pm описывает небольшой Bash-проект с такими мутациями: задает их руками под конкретные тесты, а любая выжившая мутация блокирует результат. Это опыт комментатора; для больших проектов объем и стоимость такой проверки нужно выбирать отдельно.

Что можно сделать с одним своим тестом

Я использую раздельные контексты для написания и проверки работы. Но разделение ролей не освобождает от проверки входа и эталона: два агента тоже могут независимо выбрать палиндром. Эксперимент Дэна дает хороший повод посмотреть на эту часть процесса внимательнее.

Для начала достаточно одного рискованного места:

  1. Назвать конкретную ошибку, которую тест обязан обнаружить.

  2. Выбрать вход, на котором эта ошибка изменяет результат.

  3. Получить ожидаемый ответ из контракта и записать его происхождение.

  4. В тестовой копии внести выбранную ошибку и запустить проверку.

  5. Если тест остался зеленым, выяснить причину: он не дошел до нужной ветки, вход скрыл различие или утверждение не проверяет нужное свойство.

Случайное изменение кода для этой задачи не подойдет: оно может не менять наблюдаемое поведение. Изменение должно нарушать названное требование, и внесенный баг нужно подтвердить. Проба делается в отдельной копии или временном изменении, которое затем убирается.

В статье про тихие отказы я предлагал вопрос: изменится ли сигнал проверки, если система сломается? Здесь следующий шаг - выбрать одну поломку и получить ответ запуском. Не количество тестов и не название техники, а наблюдаемое падение на конкретном нарушении.

Какую ошибку в вашем проекте тесты обязаны ловить, но вы еще не проверяли, что они действительно на нее падают?

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.