Пару месяцев назад я попросил агента проверить форму обратной связи после рефакторинга. Через несколько минут получил отчёт: все проверки пройдены, форма работает корректно. Зелёные галочки, уверенный тон.
Я тогда только начинал отдавать тестирование агенту, поэтому работал по схеме «доверяй, но проверяй»: следом прошёл форму руками, с открытой вкладкой 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
topicfield 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 обосновал: тихая потеря данных, категоризация обращений умирает молча.
Гифка — ускоренный реплей той сессии, без монтажа содержимого. Стенд лежит в репо в папке 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


