Галоўная / Артыкулы / Replacing Jest with Node's Native Test Runner in Node 24

Артыкул апублікаваны на англійскай мове.

Node.jsTypeScriptTestingJestVitestPerformance

Replacing Jest with Node's Native Test Runner in Node 24

A real-world migration shows how Node 24's built-in test runner and native TypeScript support cut CI time while removing four dependencies.

1565 слоў

Запрос да падзеўкі мела назыв «chore: remove jest, vitest config», і ў яй было адключана 340 лініяў праз чатыры devDependencies. На момент была короткая трывога, што CI запрацюе з прытвараннямі. Але гэта не адбылося. Кожны тэст быў выкананы, кожны працаваў, а звясткі пра рэгламенты тэстав продолжалі прыходзіць; весь процес завершыўся приблізна на трохце быстрэй, чым раней. Самэ тады стало ясна, што вбудованы ранер тэстаў Node ўжо не ёсць простым эксперыментам, а стаў рэальным варыянтом — такім, які калісць праігноравалі з-за прыzwычкі калькі гадоў.

Это таксама не была простая дэманстрацыйная база коду. У проекта было майже 180 файлаў тэстав, якія пакрывалі як кейсы ўнітарных, так і інтеграцыйных тэстаў, з рэальна складнымі сценарыямі мокінгу для таймераў і запытоў fetch — самэ такія сценарыі, дзе вжыванне функцыйнальнага фрэймворку здаецца безпечным стандартам.

Што на самай працоўкі змянілася

Модуль node:test пачаткова з’явіўся як експерыментальная функцыя ў Node 18, і не без прычыны ён не прывернуў большай увагі: пачатковыя версіі не мелі надзеянага падтрымкі для імітацыі, прыдатнага режыма стэрагування та выходных дадзеных пра розмах тэстав, якія здаваліся спецыяльна розробленымі, а не проста склеенымі разам. Node 24 выправляе большую частку этих проблем. Тепер данні пра розмах тэстав надходзяць безпосередньа з V8, без патрэбы на викорыстанне nyc чы c8. Режым стэрагування є настолькі розумным, што можа визначыць, якія файлы тэстав падвергаюцца вплыву певных змян, у замест на слепае перзапусканне всіх тэстав. Кроме того, імітацыя таймераў, модуляў та функцый є вбудованай, таму няма патрэбы ў дадатковых залежнасцях, каб лишь імітаваць гадзіннік чы стварыць фальшывую функцыю.

import { test, mock } from 'node:test';
import assert from 'node:assert/strict';
import { fetchUser } from './users.js';
test('fetchUser вярта нормалізаваныя даны', async (t) => {
 const fetchMock = mock.method(global, 'fetch', async () =>
 new Response(JSON.stringify({ id: 1, name: 'test' }))
 ); const user = await fetchUser(1); assert.equal(user.id, 1);
 assert.equal(fetchMock.mock.callCount(), 1);
});

Яго запускаюць за дапамою node --test — няма неабытнага файла налашчэння, няма практы Babel, няма шара трансформазій ts-jest, які б таямніча дадзе калькі секундаў да кожнага файла. Гэты апошні момент виявіўся значным больш, чым сам працоўнік тэстаў.

Натыўны частак TypeScript

Тэперь Node можа безпасобна выкананыць файлы .ts безпосередна, адзін раз зняўшы інфармацыю пра типы пад час парсавання, замест таго каб скомпіляваць іх у традыцыйны спосаб; пачынаючы з Node 24, гэта можлівасць перайшла з эксперыментальнага режыма ў стандартную для большай часткі синтаксу. Не трэба ts-node, не трэба tsx, і няма патрэбы ў окалічным кроку будовы, толькі каб запустыць скрыпты чы апытанні.

node app.ts
node --test tests/

Є сэрьзёныя прынтакі: адчысценне тыпаў не выканае жадных пераканалень тыпаў. Ён проста адмаўляе анатацыі і выканае той код, які застаўся. Якщо ваша база коду залежыць ад такіх функцый, як энумы, якія несу значэнні пад час выканання, прасторы імен або скрацэнні для параметраў канстрактара, дзеякія з іх або патрабуюць дадатковых флагаў, або ўсё ж не падтрымваюцца, адколі яны насправе даўаюць выхад у формате JavaScript, а не можуць быць цалкам адмаўленыя. Гэта не прызначана для замены кампайляра TypeScript, і ён не намагаецца гэтага. Вам яшчэ патрэбны tsc --noEmit або інструменты рэдагувальніка, каб з’яўіць рэальныя памылкі тыпаў. Тое, што гэта адмаўляе, — это давялівы час, які раней займалася кампайляцыя файлу толькі для яго выканання, ўскладненне, якое завжды было частью процесу работы з TypeScript.

Чаму набор тэстаў стаў быстрейшы

Адыяктуванне без Jest не толькі пазбавіла нас адной залежнасці, але таксама і цэлага процесу трансформацыі. По значэнню Jest обрабоцвае TypeScript адна з двух способаў: ён альбо перадае задачы ts-jest, які ўсё робіць адлюсто, але медлена, таму што выпрацоўвае повную перапачатковую перакананню типаў у кожным файле, як толькі вы не забараняйце гэта явна, альбо выкарыстоўвае babel-jest, які працюе быстрей, але дадае свой слой настройкі з сабой прынесенымі спецыяльнымі рэшэннямі для декоратараў і новейшай сынтаксісу. Калі Node выпрацоўвае файлы .ts натыўна, сам працоўнік абходзіць гэты крок трансформацыі цалкам. Да гэтага трэба дадаць тое, што node:test, як паведамляецца, працюе на 40% быстрей за пакульнія версіі працоўніка тэстаў Node для падобных заведамацтваў, а змена часу выканання всіх тэстаў на трохце ўжо не выглядае як збырак, а ёсць рэальным сумаваннем двух окремых падышч у швальнасці.

Адаптаванне аповесці працэйкі было тым элементам, які мог ўзнекаць супершчыпення. Але гэтага не сталося.

node --test --experimental-test-coverage tests/

Фарматаванне не настолькі выражанае, як тое, што Istanbul стварае ў HTML за замовчаннем, але базаваныя паказвальныкі працэйкі збігліся з цифрамі c8 на адзин пункт, і для перакрыцча запрошэнняў да змін у CI саме такі рагульнасць і є важлівым.

Дзе яно все ўсё ж застаецца позаду

Тэставанне на адпраўленыя копіі ёсць справжнім абмежэннем у гэтым случае. Команды, якія сильна павязаныя з функцыяй адпраўленых копій у Jest, чы гэта для адрасавання компонентаў чы для зафіксавання формату адпаведзей API, яшчо не знайдзяць вбудованага альтэрнатыва. Їм застаецца пісаць сабственную логіку порэвання або выкарыстоўваць окольныя бібліятэкі для адпраўленых копій, толькі каб падхапіць гэты случай. То ж самае стосуецца всіх рашэнняў, якія базуюцься на аўтаматычным мокаванні модуляў у Jest за дапамогай jest.mock('./path') з його функцыяй хоўстінгу. Функцыі mock.method і mock.module у Node адрабатваюць большую частку такіх задач, але іх выкарыстоўванне значыць неабходнасць болей часткавага і яснага падходу да таго, што самэ прыменяецца і ў які момент.

Для проекта, які выконваецца на компанентах React і ў яком выпало множыць тэстаў інтэрфейсу на адліквідацыю стана, можна спакоўвацца на тое, што гэты пераход будзе значна сложнейшы, чым у разе бэкенд-сервіса, які выкарыстоўваўся пад час гэтай ацэнкі. Змена працюе практычна без прынуджэння зараз толькі у кодзе сервера і інструментах CLI.

Паралельная експансія ўсьмо іншы факт, які варта пераканацца пры выборе гэтага падходу для большога набора тэстаў. Архітектура пулу працоўнікаў у Jest не распадзяляе файлы тэстаў між процесамі так, як гэта робіць вбудованы запускач Node, і залежна ад структуры вашага набора тэстаў, загальны час выканання можа змяніцца на небажаны спосаб у большам монорепо, нават якщо адзінаковыя файлы тэстаў працююць быстрей у ізоляцыі. Варта пераканацца гэта на рэальных машынах, якія выкарыстоўваецца вашай CI-паіплайн, а не на локальным ноутбуку з колькома вільнымя ядрамі, дзе немае іншых процэсаў, якія конкуруюць за ресурсы.

Як на самай працоўвала міграцыя

Для тых, хто рассматрывае такі перехід, ось прыблізна як усё выглядала: оба тэставання працавалі паралельна ў системе CI прыблізна тыдзень, замест таго, каб усе переключыцца заодно ў аднай заявцы на падключэння. Тыя ж файлы тэстаў праходзілі через два аднальныя задання CI, пры чым рэзултаты і час выканання практычна пораўнваліся. У ходзе гэтага процесу было выявлена два тэсты, якія непазорна выкарыстоўвалі глобальныя зменныкі, прызначаныя толькі для Jest, пра якія ніхто не памятаў, і якія былі выправлены прыблізна за гадзіну пасля выявлення. Лишыце калі два задання CI стала ўзаемна падтрымваць рэзултаты прыблізна тыдзень, тады была падана заявка на падключэння, якая выкарыстоўвала іншы інструмент замест Jest. Це сповільнены, неяскравы спосаб працэўвання, але калі мова йде пра систему безпекі, якая памагае выловіць помылкі в іншых частках, процес, які ёсць медленны, але можна вярнуць, кращы за той, які ёсць швырак, але немагчыма скасаваць.

А што з Vitest?

Эта проблема варта рашэння напраму, адколі гэта альтернатыва, яку большасць першаяя згадвае. Vitest дзейсна працуе быстрэй за Jest, апранае болей прыемны API і паслужыць часткай фронтэнд-працэйных практык, базавыхся на Vite — у гэтам няма сумневаў. Тым не менш, гэта заставае додатковую залежнасць, якая вымагае сабстычнай настройкі і можа адхіліцца ад версіі Node, якая на самай працуе, чым спрычыніць проблемы, якія займаюць цэлы вечар. Якщо кодбаза фронтэнда вже пабудавана навакол Vite, выбір Vitest застаецца разумным, адколі пакуль няма нічога власнага, што могла б заменіць тэставанне компонентаў у стыле jsdom. Аднак для бэкенд-сервіса чы ручной усталоўкі, дзе няма бандлера, включэнне Vitest толькі зарады прыемнейшай синтаксі асэрцый вярнулася без прыемнасці, калі node:test зменшыў разлік у можнасцях мокінгу і прадстаўлення коду. Гэты инструмент падходзіць толькі для аднаго конкрэтнага типу проекта, а не ўсепанальная замена для всіх.

Установка.

Які є наследкі

Це не заклекція негайна перапісваўка всіх існуючых кодавых баз. Аднак у майбутньы больш не існуе чыстаўскага стандартнага адказу на падыход з зовнішнім запускачам тэстаў пад час стварэння новага сервісу Node, што раней не было б правдападобным. Экасістэма праз майже дзесяць гадоў стварыла складную інструменталку для заполнення прасэў, якія сам рантайм залишыў незаполненымі, і тепер, калі дзеяныя з гэтых прасэў былі заполнены, частка гэй інструменталкі стала непатрэбным тыягам. Не весь інструментальны комплекс, ўсё-такі. Толькі большая частка, ніж планавалася.

Спадневана літэратура

  • Порэшчанне агентаў Frontier AI: Astra, Flash, Fable і Mythos — Дзеянне апісвае, як найновейшыя версіі модэляў GPT, Gemini і Claude выконваюць рэальныя задачы, якія трэба адрабатваць з вядомымі агентамі, такімі як програмаванне, переглед і вжыцце інструментаў, а не толькі рэзультаты тэстаў.
  • Настройка новых кантролі за чанкаваннем у Turbopack у Next.js 16.3 — У стацыі даёмаеся практычны аналіз новай настройкі turbopackChunking у Next.js 16.3, дзе пояснюецца, як параметры maxChunkCountPerGroup і generateComponentChunks вплываюць на размер пакета і кэшаванне.
  • Перапісваўка TypeScript 7 на адыгу: што гэта значыць для безпекі типаў у React — Дазвольце вам дазнацца, як кампайлер TypeScript 7, створаны на адыгу, прышвычвае процес стварэння проекта і павышае точнасьць адгадвання гэнерычных типаў, усунухаючы заштоўваныя типы any у хукіх React і JSX.
  • Інтэрцепторы NestJS: як усунуць падвышэння затрымкі на 96% у велікіх масштабах — Дазвольце вам дазнацца, як практыка выканання AOP у NestJS і проблемы з распакоўкай дадзенняў у RxJS спрычынілі рэзкое падвышэння затрымкі P99, і як стварыць інтэрцептор для аудыты раздзелавання ресурсаў, каб гэта усунуць.