Когда тест краснеет, первый инстинкт - начать менять код наугад. Профессиональная привычка обратная: сперва понять падение, потом чинить. Vitest даёт для этого целый набор средств - от графического дашборда до пошагового отладчика, - и все они служат одной цели: увидеть, что именно и почему пошло не так, прежде чем трогать реализацию. Наугад чинят долго и часто ломают соседнее; понятое падение чинится с первого раза.
Начинают с чтения самого падения. Vitest печатает Expected и Received - что ожидалось и что пришло, - и строку, на которой assert не сошёлся. Часто этого уже достаточно: видно, что число на единицу меньше, что в объекте лишнее поле, что вместо массива пришёл undefined. Стек вызовов ведёт к месту в коде под тестом. Прежде чем звать тяжёлые инструменты, стоит внимательно прочитать то, что раннер уже сказал.
Для наглядной картины есть Vitest UI - дашборд, который включают флагом --ui из пакета @vitest/ui. Он показывает дерево файлов и тестов, статусы, вывод каждого теста, граф модулей и позволяет фильтровать и перезапускать. На большом наборе это удобнее сплошной терминальной простыни: видно, какие файлы задеты, как связаны модули и что именно печатал упавший тест, без прокрутки логов.
Когда тест-файл большой, поле зрения сужают. test.only оставляет в прогоне единственный тест, describe.only - единственную группу; test.skip временно выключает, а test.todo помечает ещё не написанное. Это отсекает шум и даёт гонять только подозрительное. Важно не забыть снять only перед коммитом - иначе в CI молча выполнится один тест вместо всех, и набор перестанет что-либо стеречь.
Сужать можно и не трогая код. Флаг -t (--testNamePattern) оставляет тесты, чьё имя подходит под шаблон, а указание пути файла ограничивает прогон одним файлом. Вместе с watch это даёт быструю петлю: правишь - мгновенно видишь результат по одному тесту, не гоняя весь набор. Такой точечный запуск - основной способ держать цикл отладки коротким, пока причина не локализована.
Для настоящей отладки подключают дебаггер. Vitest запускают с --inspect-brk и обязательно с --no-file-parallelism: параллельные воркеры и пошаговая отладка несовместимы, файлы должны идти по одному. Тогда к процессу цепляют инспектор - из редактора или Chrome DevTools, - ставят точку останова и идут по шагам, глядя на реальные значения переменных вместо догадок по логам. Это дороже print-отладки, но точнее на запутанном баге.
Общий принцип сшивает всё это в метод: воспроизвести до починки. Пока падение не удаётся вызвать надёжно, чинить нечего - неясно, что именно чинишь. Поэтому сначала добиваются стабильного красного: сужают до одного теста, фиксируют вход, читают Expected/Received, при нужде идут дебаггером. Только с воспроизведённым падением правка становится осмысленной, а зелёный после неё - доказательством, что починили именно это.
Типичный провал - чинить по наитию, не прочитав Expected/Received: правка меняет симптом, а не причину, и баг всплывает рядом. Второй - забытый only, из-за которого CI гоняет один тест и пропускает регрессии в остальных. Третий - пытаться шагать дебаггером без --no-file-parallelism и удивляться, что точки останова ведут себя странно. Сначала воспроизведи и пойми, потом правь - в этом порядке отладка и коротка, и надёжна.
describe.only('корзина', () => { // гоняем только эту группу
test.only('суммирует позиции', () => { // и только этот тест
expect(total(cart)).toBe(4_200) // читаем Expected/Received
})
test.todo('применяет промокод') // ещё не написано
})vitest --ui # дашборд: дерево, статусы, граф модулей, вывод
vitest -t "скидка" # только тесты с этим именем
vitest src/cart.test.ts # только один файл
# отладчик: файлы по одному, иначе точки останова врут
vitest --inspect-brk --no-file-parallelism src/cart.test.ts