Я запретил Claude говорить «всё работает» без пруфов. Это изменило всё. claude code.. claude code. llm.. claude code. llm. Open source.. claude code. llm. Open source. playwright.. claude code. llm. Open source. playwright. qa.. claude code. llm. Open source. playwright. qa. автоматизация тестирования.. claude code. llm. Open source. playwright. qa. автоматизация тестирования. искусственный интеллект.. claude code. llm. Open source. playwright. qa. автоматизация тестирования. искусственный интеллект. тест-дизайн.. claude code. llm. Open source. playwright. qa. автоматизация тестирования. искусственный интеллект. тест-дизайн. Тестирование IT-систем.. claude code. llm. Open source. playwright. qa. автоматизация тестирования. искусственный интеллект. тест-дизайн. Тестирование IT-систем. Тестирование веб-сервисов.

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

Я тогда только начинал отдавать тестирование агенту, поэтому работал по схеме «доверяй, но проверяй»: следом прошёл форму руками, с открытой вкладкой Network. Снаружи всё честно: валидация зелёная, кнопка активная, тост «Спасибо!» показывается, сервер отвечает 200. А в payload вместо темы обращения уходит строка [object Object]. Агент проверил всё, кроме того, что реально уходит на сервер.

Я QA‑инженер, тестирую web (десктоп + мобильный адаптив), совмещаю ручное тестирование и автоматизацию на Playwright. Моя цель — максимум автоматизации и минимум ручной работы, поэтому полгода я перекладываю рутинную часть тестирования на Claude Code. Схема всё та же: агент прогоняет, я параллельно проверяю руками, и каждый его пропуск превращаю в правило — через скиллы и замечания, как дообучают стажёра. Из этих правил вырос пак скиллов, который я выложил в открытый доступ (cсылка в конце статьи). Расскажу, что внутри, и покажу демо на настоящем баге. В конце — честный список того, где это всё не работает.

Почему агенты сочиняют результаты тестирования

Строго говоря, агент не врёт. Он достраивает. Языковая модель генерирует самое правдоподобное продолжение, а самое правдоподобное продолжение отчёта о тестировании — «всё работает». Кликнул кнопку, ошибки не увидел, значит прошло. Наблюдение стоит дорого: надо снять скриншот, открыть Network, сравнить payload с контрактом. Вывод «по логике должно работать» бесплатный.

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

  • Агент читает DOM и не видит пиксели. Текст ошибки в разметке есть, а на экране он белый на белом или перекрыт другим элементом.

  • Hover‑состояния через инспекцию стайлшитов проверить нельзя. Современный Tailwind прячет :hover в @media (hover:hover), кросс‑доменные таблицы стилей бросают исключение на cssRules. Работает только реальное наведение плюс дифф computed‑стилей до и после.

  • Браузерный autofill может не триггерить событие input — поведение различается между браузерами. Поле визуально заполнено, а live‑валидация считает его пустым.

  • SSR‑гидратация. Приложения на Nuxt и Next могут перерисовать содержимое после гидратации (mismatch, клиентский дозапрос данных), и мгновенный count() ловит окно, в котором коллекция на миг пуста. Агент честно видит ноль элементов и делает неверный вывод.

  • И главное: payload. Клиентская валидация зелёная, кнопка активна, а в теле запроса [object Object], пустые поля или рубли там, где бэкенд ждёт копейки.

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

Доказательная дисциплина

Ядро пака — контракт, который агент обязан соблюдать. Вердикт Pass или Fail ставится только по наблюдённому артефакту: скриншоту, телу запроса, логу, дампу. Не проверил — пиши Not tested и причину. Мешает окружение — Blocked, и это отдельный статус, а не завуалированный Fail. Любое расхождение с макетом или требованиями логируется сразу, даже если это отступ в четыре пикселя.

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

Второе следствие менее очевидное. Требование «каждый вердикт = артефакт» заставляет агента реально ходить в Network и снимать скриншоты, потому что иначе ему нечего положить в отчёт. Дисциплина отчётности тянет за собой дисциплину проверки.

Если забирать из статьи одно — забирайте контракт. Минимальная версия вставляется в CLAUDE.md проекта как есть:

## Как тестировать
- Вердикт Pass/Fail - только по наблюдённому артефакту: скриншот, тело
  запроса/ответа, лог, дамп БД. «По логике должно работать» - не вердикт.
- Не проверил - пиши «Not tested» и почему. Мешает окружение - «Blocked»
  с причиной. Blocked не равен Fail.
- Каждое отклонение от требований/макета логируй сразу, даже минорное.
  Никаких «мелочь, уточню потом».
- Формы: проверка не заканчивается на «кнопка активна». Инспектируй
  фактический payload сабмита - [object Object], пустые и криво
  сериализованные поля, рубли вместо копеек.

Полная версия доктрины в скилле длиннее (техники тест‑дизайна, справочники, fan‑out), но 80% эффекта дают эти четыре строки.

Что такое скиллы, в двух словах

Скилл в Claude Code — это папка с файлом SKILL.md: инструкция, которую агент подгружает, когда задача совпадает с описанием. Рядом лежат references — справочники, которые он читает по мере надобности, а не держит в контексте постоянно. Никакой магии: хорошо организованный текст плюс немного конвенций.

Состав пака

Пять скиллов, они склеены в конвейер. interview собирает требования серией вопросов, когда задача описана на словах. test-cases генерит тест‑кейсы и CSV для импорта в Zephyr Scale (формат Option 1 — кто импортировал руками, тот поймёт, почему это отдельный пункт). testing гоняет проверки. bug-report заводит дефект в Jira, обязательно показав превью перед созданием. test-review проверяет свежие Playwright‑тесты до коммита.

Самое ценное лежит в справочниках скилла testing — примерно тысяча строк чеклистов, которые читаются и без всякого AI:

  • frontend: маски и лимиты полей, вставка и autofill, все состояния каждого интерактивного элемента, сверка с Figma по токенам, переполнение текста, адаптив, кросс‑браузерные ловушки WebKit и Firefox;

  • backend: семантика HTTP‑кодов, контракты, идемпотентность, транзакции и конкурентность в БД, миграции, авторизация с IDOR, очереди с DLQ, OWASP API Top 10;

  • сквозной: сетевые моки на 4xx/5xx/таймауты, консистентность UI и бэкенда, сессии и мультивкладочность, таймзоны, платежи, файлы, CSV‑инъекции при экспорте;

  • отдельный файл common‑misses: 26 проверок, которые пропускают чаще всего. Двойной сабмит, Back после успеха, эмодзи в счётчиках длины, числа больше 2^53, offline в момент отправки.

Тест‑дизайн внутри прописан до точных значений, не лозунгами. BVA — это min−1, min, min+1, max−1, max, max+1, а не «проверить граничные». Даты — это 29 февраля, 23:59:59 → 00:00:00 и несуществующее 31.02. По моему опыту, LLM неплохо знает названия техник и охотно халтурит в их применении, поэтому точные границы пришлось зашить текстом.

test-review устроен как чеклист из 49 пунктов с severity от Blocker до Nit плюс каталог правил с примерами «было → стало» и ссылками на официальную доку. Любимый пример — висящий промис:

// флак, который линт скорее всего не поймает
expect(page.getByText('Готово')).toBeVisible();   // нет await

// правильно
await expect(page.getByText('Готово')).toBeVisible();

Почему линт молчит: @typescript‑eslint/no‑floating‑promises требует type‑aware линтинга и включён мало у кого, а missing‑playwright‑await хоть и входит в recommended‑набор eslint‑plugin‑playwright, сам плагин подключён далеко не в каждом проекте. Скилл проверяет конфиг — Скилл первым делом читает ваш конфиг.

Демо на настоящем баге

Для README я собрал маленький стенд: форма обратной связи на чистом HTML плюс сервер на node без зависимостей. В форму зашит тот самый баг из начала статьи, две строки:

const topic = TOPICS.find(t => t.id === select.value); // объект {id, label}
const payload = { name, email, topic: `${topic}`, message }; // "[object Object]"

Снаружи форма безупречна: валидация отрабатывает, кнопка блокируется на время отправки, тост показывается, форма сбрасывается, консоль чистая, сервер отвечает 200. Ручной тестировщик без привычки лазить в Network пропустит. Агент без дисциплины — тем более.

Запустил прогон одной командой. Агент сделал три сабмита с разными темами, снял payload каждого и выдал отчёт: семь проверок Pass, каждая с артефактом, восьмая FAIL. Цитата из реального отчёта:

The submit flow appears to work end‑to‑end (validation passes, request sent, 200, success toast, form resets) — but the POST payload is corrupted: the topic field is serialized as the literal string "[object Object]" on every submission. This is a silent data‑integrity defect masked by a visible success.

Root cause он привёл с точностью до строк и предложил фикс (topic: topic.id). Severity обосновал: тихая потеря данных, категоризация обращений умирает молча.

Прогон скилла testing по демо-форме

Прогон скилла testing по демо‑форме

Гифка — ускоренный реплей той сессии, без монтажа содержимого. Стенд лежит в репо в папке demo/, поднимается командой node demo/server.mjs, так что весь прогон воспроизводится локально.

Отдельная ирония: пока я собирал стенд, я сам вкатил в него второй баг, незапланированный. Тост с opacity: 0 перехватывал клики по кнопке отправки, потому что я забыл pointer-events: none. Обнаружилось это, когда Playwright отказался кликать и написал, какой именно элемент перехватывает pointer events. Пункт чеклиста про оверлеи подтвердил свою нужность раньше, чем я успел его продемонстрировать.

Fan‑out для больших прогонов

На длинном флоу у одиночного агента к двадцатому экрану замыливается внимание, как и у человека. Для таких случаев в паке описан fan‑out: оркестратор один ходит по браузеру и складывает артефакты (скриншоты, дампы стилей, HAR), а потом параллельные субагенты со свежим контекстом разбирают собранное по осям. Один сверяет тексты с макетом, другой диффает токены между экранами, третий читает payload всех форм, четвёртый сравнивает Chromium с WebKit.

Браузер при этом трогает только оркестратор, иначе субагенты передерутся за навигацию. И это дорого по токенам, поэтому включаю fan‑out только на полном регрессе экрана или сверке прод/тест. Для одной страницы линейный проход дешевле и достаточен.

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

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

  • в test-review секция K — шаблон правил вашего репо: какие кастомные фикстуры обязательны, чем smoke отличается от regress, какие особенности у тестового стенда;

  • в bug-report раздел конфигурации: проект, тип задачи, обязательные кастомные поля Jira (и подсказка, как найти их id через jira_search_fields, если админ Jira в отпуске);

  • в test-cases — граница дерева папок TMS, если проект общий на несколько команд.

Где это не работает

Дотошный прогон медленный. Форма из демо в полном объёме, со всеми состояниями, негативом и адаптивом, занимает у агента 40–60 минут. По API‑тарифам такой прогон стоит единицы долларов, на подписке это просто время. Меня устраивает: прогон идёт параллельно с моей другой работой, а стоимость заметно ниже стоимости моего часа. Но «быстро глянуть» — это не сюда.

Дисциплина снижает частоту галлюцинаций, но не отменяет ревью головой. Агент может честно приложить артефакт и неверно его интерпретировать. Стало сильно реже, до нуля не дошло и вряд ли дойдёт.

Доступность (a11y/WCAG) вне scope — у меня она не тестируется, и я сознательно вырезал её из чеклистов, чтобы не имитировать экспертизу, которой в паке нет. Пак заточен под web: десктоп и мобильный адаптив. Нативные приложения, GraphQL и перф‑тестирование — в планах, пока их нет.

И последнее ограничение, честно: всё это калибровалось на моих проектах и моём стеке (Playwright + TypeScript, Jira + Zephyr). На вашем стеке углы могут торчать в других местах.

Да, я понимаю, как звучит «цель — заменить ручное тестирование» из уст ручного тестировщика. Пока агент заменил не меня, а самую механическую часть моей работы. Освободившееся время ушло туда, где руки и голова пока незаменимы: исследовательское тестирование и вопросы к требованиям, которые никто не догадался задать. Сократится ли когда‑нибудь и это — открытый вопрос, у меня нет уверенного ответа.

Ссылки

Репозиторий: github.com/akovalion/paranoid‑qa. MIT, скиллы в двух языковых версиях — русской и английской. Ставится копированием папок в ~/.claude/skills/.

Если найдёте место, где скилл выдал вердикт без пруфа — заводите issue. По определению из этой статьи, это баг.

И вопрос к вам: какие ловушки ваш агент пропускает? Файл common‑misses в паке — 28 проверок, которые чаще всего пролетают мимо. Самые злые кейсы из комментариев заберу туда с указанием авторства.

Автор: Kova13v

Источник