Переезжал с Jest 30 на Vitest и оказалось, что это не такая уж “замена одного на другое”, как кажется сначала.
В документации про это пишут, но лучше сразу быть готовым — моки работают по-разному. Простая замена jest.* на vi.* не дает того же поведения. Где-то ломается hoisting, где-то импорт происходит раньше моков, и внезапно тесты начинают вести себя иначе.
Ожидал, что Vitest будет быстрее, но это тоже не всегда так. Jest в связке с swc, может оказаться быстрее. В моем случае vitest оказался медленнее.
Vitest может показать, куда уходит время — transform, setup, import, tests, environment — и становится видно, что иногда узкое место вообще не там, где ожидаешь.
Попробовал пойти по стандартному пути — увеличить количество воркеров, поиграться с pool. Прироста не увидел. Тогда начал копать дальше и дошел до того, что Vitest по умолчанию гоняет тесты в изоляции через forks. Это безопасно, но дорого.
Отключил изоляцию и включил параллельность:
test: {
isolate: false,
pool: "threads",
sequence: {
concurrent: true,
},
}
Тесты действительно начали работать быстрее, но почти сразу всё развалилось. Начали падать тесты, которые раньше были стабильны.
Причина довольно простая — окружение перестает очищаться. DOM, моки, глобальное состояние — всё начинает течь между тестами. Документация об этом прямо говорит, но пока сам не упрешься, не очень очевидно, насколько это критично.
Даже с учетом сетап файла
afterEach(() => {
cleanup();
});
Цитирую документацию:
This greatly increases test times, which might not be desirable for projects that don't rely on side effects and properly cleanup their state (which is usually true for projects with node environment). In this case disabling isolation will improve the speed of your tests.
Нужно писать тесты так, чтобы они не зависели от побочных эффектов, очищать явно моки, и следить чтобы состояния сбрасывалось между тестами.
(В Jest это как-то все само работало). Тогда тесты будут быстрыми и получите бенефиты не только по DX, но и по скорости.
Отдельная проблема всплыла с Material UI 5. У нас используются компоненты и иконки, и пакет сам по себе
не очень дружит с ESM. Он тянет barrel файлы с реэкспортами, и в итоге импорт получается тяжелым.
Vitest это хорошо подсвечивает через метрики. В моем случае import занимал около 400ms. Замокал иконки — стало примерно 200ms, уже заметно лучше.
Между строк. Про environment. По умолчанию используется jsdom, он тяжелый. Можно попробовать заменить на happydom, если не нужны сложные DOM API. Иногда это дает ощутимый прирост.
Дальше попробовал классический подход — заменить импорты с
@mui/material на точечные
@mui/material/Button. Ожидал, что начнет подтягиваться только конкретный компонент, но эффекта не было.
Начал разбираться через devtools Vitest, и там стало видно, что резолвится node-версия пакета, внутри которой всё равно используются те же barrel файлы. То есть как ни меняй импорт, фактически подтягивается почти всё.
Оказалось, что внутри mui есть несколько сборок — node, modern, cjs. И Vitest по умолчанию берет не ту, которая лучше всего подходит для tree-shaking.
Дальше идея — попробовать алиас на modern сборку и посмотреть, даст ли это выигрыш по import времени. В моем случае замена на modern только ухудшила метрики import, из вариантов улучшить, это попробовать перенести babel-plugin-import на rolldown.
Итоги переезда вскрыли проблемы импортов (я еще нашел циклические импорты), старых зависимостей, то как пишутся тесты.
Вывод такой, нужно держать руку на пульсе и следить за тех долгом. Современные, быстрые инструменты улучшают DX, это как и локальный запуск, так и быстрый cicd, лучше фидбек что идет не так.