Почему баг Cursor — это не обычный баг
Человек, который пишет то же самое изменение, держит в голове контекст, недоступный редактору. Он помнит, что панель настроек читает из кеша, который нужно инвалидировать; что кнопка загрузки неактивна, пока не выбран файл; что конкретный эндпоинт при отсутствии совпадений возвращает пустой массив, а не 404. Cursor исходит из файлов, которые вы открыли, и текста, который вы набрали. Всё, что за пределами этого окна, — догадка.
Поэтому дело не в кривом синтаксисе. Проблема в изменении, которое локально корректно и глобально неверно. Функция делает ровно то, что заявлено. Но вызывается она не в тот момент, или оставляет после себя кусок состояния, или рассчитывает на структуру, которую API перестал возвращать два спринта назад.
Юнит-тесты этого не ловят, потому что пишутся исходя из тех же предположений, что и код. Если и то и другое написал агент, тесты согласуются с кодом — и расходятся с вашим продуктом.
Откуда баги Cursor берутся на самом деле
Большая часть приходится на три области.
Состояние интерфейса
Форма отправляется, но список за ней не обновляется.
Модальное окно закрывается и оставляет блокировку прокрутки на body.
Кнопка остаётся активной, пока запрос уже выполняется, и двойной клик создаёт две записи.
Асинхронные сценарии
Экран отрисовывается до прихода данных и больше не перерисовывается.
Фоновая задача запускается, но её никто не дожидается, и следующий шаг читает устаревшие значения.
Ветка с ошибкой завершается молча, и пользователь видит сообщение об успехе там, где операция провалилась.
Границы интеграций
Тело запроса соответствует описанию типа, но не тому, что на самом деле принимает сервис.
Аутентификация считается выполненной, потому что она была выполнена в той сессии, которую видел агент.
Пагинация, пустые состояния и лимиты запросов обработаны в системе типов — и больше нигде.
Общее у всех этих случаев одно: увидеть их можно, только запустив приложение и поработав в нём. Чтение диффа не покажет ни одного из них — как и набор тестов, который ни разу не открывает браузер.
Как поймать баги Cursor до слияния
Проверка должна идти по развёрнутому приложению, причём работать с ним нужно так, как работал бы человек, а результат должен возвращаться в виде, пригодном для действий агента. Иначе баг найден, но чинить его всё равно вам.
Должны выполняться три условия. Ни одно из них не сложное, но стоит выпустить любое — и цикл начинает протекать.
Агент должен уметь проверять
Установка добавляет навык проверки прямо в кодинг-агента, поэтому он знает, как создавать, запускать и разбирать тесты, а не догадывается об этом по README. Она пишет файл с инструкциями туда, где ваш редактор его и так ищет, — то есть Cursor подхватывает его из .cursor/rules/ так же, как Claude Code читает собственный каталог навыков. Поддерживаются восемь редакторов — это важно в команде, где не все работают в одном и том же.
Терминал
npm install -g @testsprite/testsprite-cli
testsprite setup
То же самое можно сделать в дашборде TestSprite, если не хочется ничего ставить локально. Всё остальное, что умеет CLI, описано в репозитории CLI.
Проверка должна идти по развёрнутому приложению, а не по моку
Направьте запуск на окружение, где изменение уже работает. Агент открывает приложение, проходит сценарий как пользователь и сообщает, что произошло на самом деле, а не выполнилось ли утверждение в песочнице. Моки такого сигнала дать не могут — поэтому зелёные юнит-тесты и сломанная функциональность так спокойно уживаются вместе.
Падение должно возвращаться в том виде, с которым агент может работать
Именно здесь решается, замкнётся ли цикл. Если падение приходит скриншотом в дашборде, его должен прочитать, истолковать и превратить в промпт человек. Если оно приходит единым связным пакетом — что пытались сделать, как повело себя приложение и где именно возникло расхождение, — агент может взять его и сразу действовать. Следующая итерация начинается с фактов, а не с вашего пересказа фактов.
Сделайте так, чтобы никому не приходилось об этом помнить
Проверку, которую запускают руками, пропускают в тот самый день, когда все спешат, — то есть когда она нужнее всего. Автоматизировать её можно двумя способами, и выбор зависит от того, кто отвечает за ваш пайплайн.
GitHub App — это вебхук, который настраивается в дашборде TestSprite. Он слушает событие деплоя, которое ваш пайплайн уже генерирует, поэтому в репозитории ничего менять не нужно.
GitHub Actions встраивает этот шаг прямо в ваш workflow, а настраивается из терминала.
Саму схему работы стоит понимать, даже если вы никогда не заглянете в конфигурацию. Интеграция не собирает и не деплоит ваше приложение, не добавляет и не редактирует файлы workflow. Она слушает событие деплоя, которое ваш пайплайн уже генерирует, и воспринимает «новая сборка доступна по этому URL» как сигнал к запуску. Результаты возвращаются туда, где идёт работа, — комментарием к пул-реквесту или проверкой на коммите.
Полезность зависит от двух решений, и оба — вопрос здравого смысла, а не настроек.
Какой момент считать готовностью. Выбирайте событие, которое срабатывает после того, как деплой стал доступен, а не в момент старта сборки. Слишком раннее событие направит запуск на URL, который ещё не поднялся, и все тесты упадут по причине, никак не связанной с вашим кодом. Это самая частая ошибка при настройке.
Проверка рекомендательная или обязательная. Комментарий к пул-реквесту информирует. Обязательная проверка блокирует слияние. Начните с рекомендательного режима на неделю, посмотрите, что она ловит, и тогда решайте. Сделать её обязательной с первого дня, пока ей ещё никто не доверяет, — верный способ добиться того, что полезную проверку отключат.
Практическое замечание, если ваши превью живут на площадке вроде Vercel или Netlify: у каждого хостинга свои правила именования URL превью, поэтому интеграция принимает шаблон, а не фиксированный адрес, и подставляет ветку или пул-реквест при каждом запуске.
Как это соотносится с вашими текущими тестами
Это поведенческая проверка работающего продукта, поэтому она дополняет быстрые локальные юнит-тесты, а не заменяет их. Оставьте юнит-тесты для логики, а этой проверке отдайте то, что они по своей природе увидеть не могут: работает ли функциональность, когда ею пользуется реальный человек.
Замечание о более общей закономерности
Всё это не специфично для Cursor. Тот же разрыв возникает с любым ассистентом, который пишет код быстрее, чем человек успевает его читать, — поэтому шаг проверки стоит выстроить один раз и переиспользовать во всех редакторах. В открытом рейтинге, где агенты собирали одно и то же приложение, самая дешёвая модель из всех выдала наиболее корректный результат именно с таким циклом проверки — вдвое дешевле самого дорогого участника. Вывод не в том, что одна модель лучше другой. Вывод в том, что модель с работающим сигналом обратной связи обыгрывает более сильную модель без него.
Где TestSprite встраивается в цикл работы с Cursor
TestSprite — это половина, отвечающая за проверку. Cursor пишет изменение; TestSprite открывает развёрнутое приложение, проходит сценарий так, как это сделал бы пользователь, и сообщает, что произошло на самом деле. Ни один не пытается делать работу другого, и в этом разделении весь смысл: автор изменения — неподходящая сторона для его подтверждения.
Для команды, которая выкатывает код, написанный Cursor, отсюда следует три вещи. Покрытие перестаёт зависеть от того, найдёт ли кто-то время писать тесты: кейсы генерируются по вашему продукту и уточняются обычным языком. Косметические редизайны перестают красить набор тестов в красный, потому что шаг — это намерение, а не путь по DOM. А падение возвращается единым пакетом, с которым агент может работать, так что следующая итерация начинается с фактов, а не с вашего их пересказа.
В первый же день вы получаете сценарии, сломать которые было бы стыдно: они прогоняются на каждом пул-реквесте, а результат называет конкретное расхождение. Чего сервис не делает — так это не проверяет ваш код и не заменяет быстрые локальные юнит-тесты; он работает рядом с тем и другим.
Почему тесты, написанные Cursor, проходят, хотя функциональность сломана?
Потому что тесты и код выросли из одних и тех же предположений. Если агент решил, что эндпоинт возвращает пустой массив, и написал вокруг этого и обработчик, и тест, они согласуются друг с другом. Разрешить спор может только запуск настоящего приложения против настоящего сервиса.
Нужна ли команда QA, чтобы это настроить?
Нет. Вся настройка — это одна команда CLI и установка GitHub App. Всё рассчитано на команды, где результаты тестов смотрят только разработчики.
Нужен ли доступ к моему исходному коду?
Проверка идёт по развёрнутому приложению через его интерфейс. Право на запись в GitHub нужно для того, чтобы публиковать результаты в пул-реквестах и коммитах, а не чтобы пушить код или менять файлы workflow.
Что будет с тестами, если интерфейс изменили намеренно?
Тест, привязанный к бизнес-сценарию, а не к конкретному элементу, переживает редизайн, который сам сценарий не меняет. Если же меняется сам сценарий, это новое поведение: нужен новый или обновлённый кейс, и агент может сгенерировать его по изменению.
Можно ли запускать это на ветке, не затрагивая основной набор тестов?
Тесты принадлежат приложению, а не ветке в git, поэтому набор всегда один, канонический. Выберите триггер по пул-реквесту, если нужны проверки по веткам, и триггер по push, если нужно проверять одно общее окружение после каждого слияния.
Хватит читать дифф. Запустите приложение.
Баги Cursor прячутся в состоянии, таймингах и на границах интеграций — ровно там, куда не дотягиваются ни чтение кода глазами, ни тест на моках. Отдайте проверку в руки агента, направьте её на развёрнутую сборку и пусть пул-реквест сам скажет вам, работает ли изменение, ещё до того, как кто-то его вольёт.