راهنمای مهاجرت Vitest در مقابل Jest 2026 با معیارهای واقعی

مقایسه Vitest و Jest
| ابعاد | شوخی ۳۰ | ویتست ۳.x |
|---|---|---|
| استارت سرد (50 هزار تست) | ۲۱۴ | ۳۸ ثانیه (۵.۶ برابر سریعتر) |
| حالت تماشا، اجرای مجدد | ۸.۴ ثانیه (هش کردن فایل) | ۰.۳ ثانیه (نمودار HMR) |
| پشتیبانی ESM | به پرچمهای پیکربندی نیاز دارد | بومی از طریق خط لوله Vite |
| پیچیدگی پیکربندی | بالا (تبدیلها، نگاشتکنندهها، محیطها) | پایین (از vite.config.ts ارثبری میکند) |
اگر زمانی را صرف کلنجار رفتن با خط لوله تبدیل Jest در یک کدبیس TypeScript با ESM سنگین کردهاید، از قبل با دردسر آن آشنا هستید. Vitest در مقابل Jest در سال ۲۰۲۶ یک بحث انتزاعی نیست - این یک سوال عملی است که هر روز بر سرعت تیم شما، هزینه CI شما و تجربه توسعهدهنده شما تأثیر میگذارد.
فهرست مطالب
Jest به دلایل خوبی تسلط خود را به دست آورد. این فریمورک، اجرای تست، mocking، assertionها و پوشش را زیر یک سقف یکپارچه کرد، زمانی که اکوسیستم جاوااسکریپت به شدت به این ادغام نیاز داشت. نظرسنجیهای وضعیت جاوااسکریپت این موضوع را تأیید میکنند: Jest به طور مداوم پرکاربردترین فریمورک تست جاوااسکریپت بوده است که سالهاست در اکثر پروژههای جاوااسکریپت استفاده میشود. این میراث مهم است. اما اکوسیستم به مسیر خود ادامه داد. Vite به سرور توسعه پیشفرض و ابزار ساخت برای اکثر فریمورکهای مدرن تبدیل شد. Native ESM به فرمت ماژول مورد انتظار تبدیل شد. TypeScript به یک پایه تبدیل شد، نه یک افزونه.
Vitest فقط یک Jest سریعتر نیست. از نظر معماری با جایی که زنجیره ابزار جاوا اسکریپت از قبل قرار دارد، همسو است. روی خط لوله تبدیل Vite اجرا میشود، نامهای مستعار و افزونههای vite.config.ts شما را به اشتراک میگذارد و با ESM و TypeScript به عنوان شهروندان درجه یک بدون پیکربندی اضافی رفتار میکند.
این مقاله سه چیز را ارائه میدهد: معیارهای عملکرد رو در رو از یک کدبیس تولید واقعی با ۵۰،۰۰۰ تست، یک راهنمای گام به گام مهاجرت به Jest با نکات مبهمی که هیچ کس دیگری مستند نمیکند، و یک چارچوب تصمیمگیری تا بتوانید تصمیم درست را برای موقعیت خاص خود بگیرید.
وضعیت هر دو چارچوب در سال ۲۰۲۶
شوخی ۳۰: چه چیزی تغییر کرده است
Jest 30 پیشرفتهای معناداری را به همراه داشت. پشتیبانی از ESM نسبت به دوران Jest 29 مستندسازی بهتر و پایدارتر شده است، و الگوی import @jest/globals مسیر تمیزتری نسبت به رویکرد قدیمی globals ضمنی ارائه میدهد. تیم Jest راهنماییهای دقیقی در مورد ESM منتشر کرده است که تعامل بین تبدیلها، قالبهای ماژول و رفتار ESM خود Node را پوشش میدهد.
اما معماری اساسی تغییر نکرده است. Jest در دنیای CommonJS ساخته شده است. هر ماژول ESM هنوز از یک خط لوله تبدیل عبور میکند که کد را در فرآیندهای کارگر سریالسازی و از سریالسازی خارج میکند. سطح پیکربندی همچنان بزرگ است: transform ، moduleNameMapper ، extensionsToTreatAsEsm ، transformIgnorePatterns ، و تعامل بین این کلیدها یک پیچیدگی ترکیبی ایجاد میکند که مرتباً تیمها را آزار میدهد. سرعت توسعه در مقایسه با تکرار سریعی که Vitest از آن برخوردار است، کند شده است.
Vitest 3.x: چه چیزی بالغ شده است
Vitest به خوبی از مرحله «تازهکار نویدبخش» عبور کرده است. پشتیبانی از فضای کاری برای monorepos آماده تولید است. حالت مرورگر، اجرای تست مبتنی بر مرورگر واقعی را بدون تقریبهای jsdom به شما ارائه میدهد. بهبودهای اسنپشات درونخطی و بلوغ سیگنال سطح API پایدار.
پذیرش اکوسیستم به وضوح داستان را بیان میکند. Nuxt، SvelteKit و Astro همگی به طور پیشفرض برای تنظیمات تست خود از Vitest استفاده میکنند. Angular به سمت پشتیبانی از Vitest حرکت کرده است و تیم Angular یک سازنده Vitest آزمایشی ارائه میدهد. وقتی توسعهدهندگان چارچوبها واقعاً از یک ابزار تست استفاده میکنند، این قویترین سیگنالی است که جامعه تولید میکند.
Vitest فقط یک Jest سریعتر نیست. بلکه از نظر معماری با جایی که زنجیره ابزار جاوا اسکریپت از قبل قرار دارد، همسو است.
یک نکتهی مهم: مدیریت TypeScript در Vitest به صورت "اجرای بومی TypeScript" نیست. TypeScript را از طریق خط لولهی پلاگین Vite پردازش میکند، که معمولاً از esbuild برای انتقال سریع استفاده میکند. این تمایز مهم است زیرا بررسی نوع در طول اجرای تست اتفاق نمیافتد. شما هنوز به tsc یا ابزاری مشابه برای آن نیاز دارید. اما برای سرعت اجرای تست، تبدیل مبتنی بر esbuild به طور چشمگیری سریعتر از کاری است که ts-jest انجام میدهد.
در عمل، تفاوت پیکربندی برای یک پروژه TypeScript به همراه React به این شکل است:
// jest.config.ts import type { Config } from 'jest'; const config: Config = { testEnvironment: 'jsdom', transform: { '^.+\\.tsx?$': ['@swc/jest', { jsc: { parser: { syntax: 'typescript', tsx: true }, transform: { react: { runtime: 'automatic' } } } }] }, moduleNameMapper: { '^@/(.*)$': '<rootDir>/src/$1', '\\.(css|less|scss)$': 'identity-obj-proxy' }, setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'], extensionsToTreatAsEsm: ['.ts', '.tsx'] }; export default config;// vitest.config.ts import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], resolve: { alias: { '@': new URL('./src', import.meta.url).pathname } }, test: { environment: 'jsdom', setupFiles: ['./src/setupTests.ts'], globals: true } });// jest.config.ts import type { Config } from 'jest'; const config: Config = { testEnvironment: 'jsdom', transform: { '^.+\\.tsx?$': ['@swc/jest', { jsc: { parser: { syntax: 'typescript', tsx: true }, transform: { react: { runtime: 'automatic' } } } }] }, moduleNameMapper: { '^@/(.*)$': '<rootDir>/src/$1', '\\.(css|less|scss)$': 'identity-obj-proxy' }, setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'], extensionsToTreatAsEsm: ['.ts', '.tsx'] }; export default config;
// vitest.config.ts import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react';
export default defineConfig({ plugins: [react()], resolve: { alias: { '@': new URL('./src', import.meta.url).pathname } }, test: { environment: 'jsdom', setupFiles: ['./src/setupTests.ts'], globals: true } }); پیکربندی Vitest، مدیریت CSS، نامهای مستعار مسیر و تبدیلهای JSX/TypeScript را از خط لوله Vite به ارث میبرد. هیچ بسته ترانسفورماتور جداگانهای وجود ندارد. هیچ regex moduleNameMapper وجود ندارد. هیچ extensionsToTreatAsEsm وجود ندارد. سطح پیکربندی کوچک میشود زیرا اجراکننده تست شما همان خط لوله تبدیل را با سرور توسعهدهنده شما به اشتراک میگذارد.
روش بنچمارک: چگونه آزمایش کردیم
ما تستهایی را روی یک مونوریپوی عملیاتی که شامل تقریباً ۵۰،۰۰۰ تست در ۸ پکیج بود، اجرا کردیم. کدبیس، تستهای کامپوننت React (با استفاده از Testing Library)، تستهای واحد سرویس Node و تستهای یکپارچهسازی را که روی پایگاههای داده درون حافظه اجرا میشوند، ترکیب میکند. در کل از TypeScript و فرمت ماژول ESM استفاده شده است.
مشخصات سختافزاری و CI:
دستگاه محلی: مکبوک پرو M2، رم ۱۶ گیگابایتی، Node 22.x LTS
CI: اقدامات گیتهاب ubuntu-latest (اجراکنندههای استاندارد لینوکس با vCPUهای موجود که بسته به طرح متفاوت هستند)
آنچه ما اندازهگیری کردیم:
مجموعه کامل شروع سرد (بدون حافظه پنهان، فرآیند تازه)
اجرای مجدد حالت تماشا (تغییر تک فایل به سبز/قرمز)
اجرای موازی در فضای کاری (هر ۸ بسته به طور همزمان)
کل زمان خط لوله CI (اقدامات GitHub از ابتدا تا انتها)
ابزارهای اندازهگیری: ما از hyperfine --warmup 1 --runs 5 برای بنچمارکهای محلی، CI timestamps از لاگهای کار GitHub Actions و ردیابی حافظه فرآیند از طریق process.memoryUsage() در Node استفاده کردیم. هر دو فریمورک برای تستهای زمینه مرورگر با محیط jsdom و برای تستهای سرویس node اجرا شدند. هر دو از ارائهدهنده پوشش V8 استفاده کردند. Jest از @swc/jest برای تبدیلها استفاده کرد تا مقایسه در لایه تبدیل منصفانه باشد، زیرا Vitest از خط لوله esbuild در Vite استفاده میکند.
کنترلها: ما حافظه پنهان (cache) را بین اجراهای شروع سرد برای هر دو فریمورک پاک کردیم. تعداد Workerها مطابقت داشت: Jest با --maxWorkers=4 و Vitest با pool: 'threads' و poolOptions.threads.maxThreads: 4 نسخه Node در تمام اجراها یکسان بود.
# Jest benchmarking commands hyperfine --warmup 1 --runs 5 \ 'npx jest --clearCache && npx jest --maxWorkers=4 --coverage'
# Vitest benchmarking commands hyperfine --warmup 1 --runs 5 \ 'npx vitest run --pool threads \ --poolOptions.threads.maxThreads=4 --coverage'
# Watch mode: measured via timestamp injection on file save # to test reporter output (custom script, not hyperfine)سلب مسئولیت: این نتایج جهتدار هستند. اعداد شما بسته به شکل پروژه، پیچیدگی تست، عمق نمودار ماژول و سختافزار متفاوت خواهد بود. ما روش خود را منتشر میکنیم تا بتوانید آن را با کدبیس خود مقایسه و تکثیر کنید.
نتایج بنچمارک: اعداد
سوئیت کامل با استارت سرد
| متریک | جست ۳۰ (@swc/jest) | ویتست ۳.x | دلتا |
|---|---|---|---|
| کل زمان (50 هزار تست) | ۲۱۴ | ۳۸ ثانیه | ۵.۶ برابر سریعتر |
| حافظه اوج (RSS) | ۴.۲ گیگابایت | ۱.۸ گیگابایت | ۵۷٪ کمتر |
| میانگین استفاده از پردازنده | ۶۸٪ | ۹۱٪ | اشباع بهتر |
این شکاف از دو جا ناشی میشود. اول، خط لوله تبدیل مبتنی بر Vite در Vitest، سربار سریالسازی/دسریالزدایی را که مدل فرآیند کارگر Jest به آن نیاز دارد، حذف میکند. Jest فرآیندهای کارگر را منشعب میکند که هر کدام به طور مستقل ماژولها را تبدیل و اجرا میکنند، به این معنی که یک فایل میتواند چندین بار بین کارگران تجزیه شود. Vitest از یک مخزن نخ با یک نمودار ماژول مشترک استفاده میکند، بنابراین ماژولهای تبدیل شده به طور کارآمدتری ذخیره و به اشتراک گذاشته میشوند. دوم، ماژولهای ESM در Vitest از لایه interop CJS که Jest هنوز به طور داخلی به آن متکی است، عبور نمیکنند و یک کلاس کامل از سربار را حذف میکنند.
بر اساس بنچمارکهای کوچکتری که آنلاین دیده بودم، انتظار داشتم این تفاوت حدود ۳ برابر باشد. اما اختلاف ۵.۶ برابری ما را شگفتزده کرد. توضیح احتمالی: نمودار ماژول ما عمیق است (بهطور متوسط زنجیره وابستگی ۱۲ ماژول در هر فایل آزمایشی)، که مزیت ذخیرهسازی تبدیل را تقویت میکند.
حالت تماشا: تغییر تک فایل، اجرای مجدد
| متریک | شوخی ۳۰ --تماشا کنید | ویتست --ساعت | دلتا |
|---|---|---|---|
| زمان از ذخیره تا نتیجه | ۸.۴ ثانیه | ۰.۳ ثانیه | ۲۸ برابر سریعتر |
| آزمایشها دوباره اجرا میشوند | ۳۴۰~ (اکتشافی) | ~12 (نمودار HMR) | ۹۶٪ کمتر |
اینجاست که تفاوت معماری به شدت خود را نشان میدهد. حالت watch در Jest از هش کردن فایل و روشهای اکتشافی برای تشخیص اینکه کدام تستها باید دوباره اجرا شوند، استفاده میکند. این حالت در اجرای تستهای بیشتر از حد لازم اشتباه میکند، زیرا نمیتواند نمودار وابستگی را به طور دقیق در جزئیات تفکیکپذیری ماژول ردیابی کند. Vitest از نمودار وابستگی HMR در Vite استفاده میکند که دقیقاً میداند کدام ماژولها فایل تغییر یافته را وارد میکنند، بنابراین فقط تستهایی را که واقعاً تحت تأثیر قرار گرفتهاند، دوباره اجرا میکند. نتیجه فقط اجرای سریعتر نیست، بلکه تعداد تستهای اجرا شده به ازای هر تغییر به طرز چشمگیری کمتر است.
Vitest از نمودار وابستگی HMR Vite استفاده میکند، که دقیقاً میداند کدام ماژولها فایل تغییر یافته را وارد میکنند، بنابراین فقط تستهایی را که واقعاً تحت تأثیر قرار گرفتهاند، دوباره اجرا میکند.
اجرای موازی در فضای کاری
| متریک | پروژهها - جست ۳۰ | فضاهای کاری ویتست | دلتا |
|---|---|---|---|
| بسته کامل ۸ عددی | ۱۸۷ ها | ۴۲ ثانیه | ۴.۵ برابر سریعتر |
| حداکثر حافظه (کل) | ۶.۸ گیگابایت | ۲.۴ گیگابایت | ۶۵٪ کمتر |
پیکربندی --projects در Jest، هر پروژه را به عنوان یک نمونه Jest تا حدودی مستقل اجرا میکند. ویژگی فضای کاری Vitest، سرور Vite و حافظه پنهان ماژول را در بین بستهها به اشتراک میگذارد و کار اضافی را در monorepos با وابستگیهای مشترک به میزان قابل توجهی کاهش میدهد.
کل زمان اجرای خط لوله CI (اقدامات گیتهاب)
| متریک | شوخی ۳۰ | ویتست ۳.x | دلتا |
|---|---|---|---|
| کل مدت زمان کار | ۱۲ دقیقه و ۱۸ ثانیه | ۳ دقیقه و ۴۲ ثانیه | ۳.۳ برابر سریعتر |
| دقیقه/کارکرد قابل پرداخت | ۱۳ | ۴ | ۶۹٪ کاهش |
| هزینه تخمینی ماهانه (۲۰۰ اجرا) | ۵۲ دلار | ۱۶ دلار | ۳۶ دلار در ماه صرفهجویی شد |
بهبود CI کمتر از تفاوت شروع سرد محلی است زیرا سربار CI (پرداخت، نصب وابستگی، آپلود مصنوعات) صرف نظر از اجراکننده تست ثابت میماند. اما صرفهجوییها بیشتر میشود. برای تیمی که ۲۰۰ کار CI را در ماه انجام میدهد، این مبلغ تقریباً ۴۳۰ دلار در سال فقط در دقایق GitHub Actions میشود. هزینه واقعی، زمان انتظار توسعهدهنده است که بسیار گرانتر است.
نکتهای در مورد این تخمینهای هزینه CI: قیمتگذاری GitHub Actions بر اساس طرح و نوع runner متفاوت است. ارقام بالا بر اساس نرخ استاندارد در هر دقیقه برای runnerهای لینوکس در یک طرح پولی است. هزینههای واقعی شما به طرح GitHub و پیکربندی runner شما بستگی دارد.
Vitest vs Jest 2026: خلاصه بنچمارک
| سناریو | شوخی ۳۰ | ویتست ۳.x | بهبود |
|---|---|---|---|
| استارت سرد (تست ۵۰ هزار) | ۲۱۴ | ۳۸ ثانیه | ۵.۶ برابر |
| حالت تماشا دوباره اجرا میشود | ۸.۴ ثانیه | ۰.۳ ثانیه | ۲۸ برابر |
| مونورپو (۸ بسته) | ۱۸۷ ها | ۴۲ ثانیه | ۴.۵ برابر |
| خط لوله CI | ۱۲ دقیقه و ۱۸ ثانیه | ۳ دقیقه و ۴۲ ثانیه | ۳.۳ برابر |
روی Node 22.x LTS، GitHub Actions ubuntu-latest، ۴ worker، محیط jsdom و پوشش V8 تست شده است.
مقایسه ویژگی به ویژگی
| ویژگی | شوخی ۳۰ | ویتست ۳.x |
|---|---|---|
| پشتیبانی ESM | مستند شده اما به پرچمهای پیکربندی نیاز دارد | بومی از طریق خط لوله Vite |
| تایپ اسکریپت | به ترانسفورماتور @swc/jest یا ts-jest نیاز دارد | از طریق Vite (esbuild)، بدون نیاز به پیکربندی اضافی |
| حالت تماشا | روشهای اکتشافی هش کردن فایل | نمودار وابستگی HMR وایت |
| تست اسنپشات | پشتیبانی کامل | قالب سازگار، به عنوان دراپ-این کار میکند |
| مسخره کردن | jest.mock() با قابلیت بالا بردن | vi.mock() با قابلیت بالا بردن + واردات خودکار |
| تست مرورگر | jest-environment-jsdom | حالت مرورگر داخلی (مرورگرهای واقعی) |
| پوشش کد | --coverage (استانبول/V8) | --coverage (استانبول/V8) از طریق @vitest/coverage-v8 |
| پشتیبانی مونورپو | --projects (نمونههای جداگانه) | فضاهای کاری (سرور Vite مشترک) |
| انجمن/افزونهها | اکوسیستم عظیم و بالغ | رشد سریع، سازگار با افزونه Vite |
| پیچیدگی پیکربندی | بالا (تبدیلها، نگاشتکنندهها، محیطها) | کم (از پیکربندی Vite به ارث رسیده است) |
| آزمایش ایزولاسیون | فرآیندهای کارگر را جدا کنید | مخزن نخ با تنظیم مجدد ماژول |
| مدل کارگر | فرآیندهای فرزند | رشتهها (پیشفرض) یا انشعابها |
جایی که جست هنوز برنده است
اکوسیستم تطبیقدهندههای سفارشی و افزونههای اجتماعی Jest بسیار عظیم و آزمایششده است. کتابخانههایی مانند jest-extended ، سریالایزرهای سفارشی برای اسنپشاتها و ابزارهای تست مخصوص فریمورک، سالها راهحلهای پیشرفتهای را در خود جای دادهاند. اگر تیم شما به یک افزونهی Jest خاص متکی است که معادل Vitest ندارد، این یک مانع واقعی برای مهاجرت است.
کدهای سازمانی با الگوهای ناهمگام پیچیده، اجراکنندههای تست سفارشی یا پیکربندیهای jest-circus که عمیقاً سفارشیسازی شدهاند، دانش نهادی را انباشتهاند که تکرار آن پرهزینه است. اجراکننده jest-circus (که در Jest 27 به پیشفرض Jest تبدیل شد) سالها راهحلهای پایداری برای تعاملات پیچیده setTimeout ، Promise و process.nextTick دارد.
Jest همچنین به عنوان پیشفرض برای پروژههای Next.js باقی میماند. اگر از Next.js به همراه Webpack استفاده میکنید (نه Turbopack یا Vite)، استفاده از Jest از سربار نگهداری یک پیکربندی جداگانه Vite صرفاً برای آزمایش جلوگیری میکند. این ممکن است با تکامل اکوسیستم Next.js تغییر کند، اما در حال حاضر، اگر پروژه Next.js شما از Vite استفاده نمیکند، قبل از اقدام به تغییر، هزینه تغییر را با دقت بسنجید.
راهنمای مهاجرت: گام به گام
مرحله ۱: نصب Vitest و حذف وابستگیهای Jest
# Install Vitest and coverage provider npm install -D vitest @vitest/coverage-v8 jsdom
# Remove common Jest-related packages npm uninstall jest ts-jest @swc/jest babel-jest \ @types/jest jest-environment-jsdom \ identity-obj-proxy @jest/globals تفاوت package.json شما چیزی شبیه به این خواهد بود:
"devDependencies": { - "jest": "^30.0.0", - "@swc/jest": "^0.2.37", - "@types/jest": "^29.5.0", - "jest-environment-jsdom": "^30.0.0", - "identity-obj-proxy": "^3.0.0", + "vitest": "^3.1.0", + "@vitest/coverage-v8": "^3.1.0", + "jsdom": "^25.0.0" }, "scripts": { - "test": "jest", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage" + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage" }اینکه دقیقاً چه بستههایی را حذف میکنید، به پروژه شما بستگی دارد. برخی ممکن است وابستگیهای انتقالی باشند. اگر مطمئن نیستید، فایل قفل خود را بررسی کنید.
مرحله 2: ترجمه پیکربندی
نگاشتهای کلید از jest.config.ts به vitest.config.ts :
// vitest.config.ts — translated from a realistic Jest config import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react'; import { fileURLToPath, URL } from 'node:url';
export default defineConfig({ plugins: [react()],
// moduleNameMapper → resolve.alias resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), '@components': fileURLToPath(new URL('./src/components', import.meta.url)), '@utils': fileURLToPath(new URL('./src/utils', import.meta.url)), // CSS/asset mocks are unnecessary — Vite handles them } },
test: { // testEnvironment → environment environment: 'jsdom',
// setupFilesAfterEnv → setupFiles setupFiles: ['./src/setupTests.ts'],
// Enable Jest-like global APIs (describe, it, expect) // without explicit imports in every file globals: true,
// Coverage configuration (was top-level in Jest) coverage: { provider: 'v8', reporter: ['text', 'lcov'], include: ['src/**/*.{ts,tsx}'], exclude: ['src/**/*.test.{ts,tsx}', 'src/**/*.d.ts'], // coverageThreshold → thresholds thresholds: { statements: 80, branches: 75, functions: 80, lines: 80 } },
// Include patterns (testMatch → include) include: ['src/**/*.test.{ts,tsx}'] } }); چند نکته در این مقایسه چارچوب تست جاوا اسکریپت شایان ذکر است. واردات CSS و داراییهایی که در Jest به الگوهای identity-obj-proxy یا moduleNameMapper نیاز داشتند، به طور خودکار توسط pipeline ویت مدیریت میشوند. پیکربندی transformIgnorePatterns که اغلب در Jest برای وابستگیهای ESM در node_modules ضروری است، معمولاً در Vitest غیرضروری است زیرا پیشبستهبندی وابستگی Vite این مشکل را مدیریت میکند. اگر با وابستگیهای خاص به مشکل برخوردید، Vitest server.deps.inline (یا deps.inline در پیکربندی تست) را به عنوان دستگیره معادل ارائه میدهد.
اگر globals: true استفاده میکنید، tsconfig.json خود را بهروزرسانی کنید تا تعاریف نوع Vitest را شامل شود:
{ "compilerOptions": { "types": ["vitest/globals"] } } این جایگزین @types/jest میشود و تضمین میکند که describe ، it ، expect و vi بدون وارد کردن صریح توسط TypeScript شناخته میشوند.
مرحله ۳: بهروزرسانی فایلهای آزمایشی
سطح API تقریباً یکسان است. یک تابع یافتن و جایگزینی سراسری ۹۰٪ کار را انجام میدهد:
jest.fn() تبدیل به vi.fn() میشود.
jest.mock() تبدیل به vi.mock() میشود.
jest.spyOn() تبدیل به vi.spyOn() می شود
jest.useFakeTimers() تبدیل به vi.useFakeTimers() می شود
jest.clearAllMocks() تبدیل به vi.clearAllMocks() میشود.
اگر globals: true استفاده نمیکنید، import را به هر فایل آزمایشی اضافه کنید:
import { describe, it, expect, vi } from 'vitest';در اینجا یک تست کامپوننت React معمولی قبل و بعد از آن آورده شده است:
// BEFORE: Jest version import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { UserProfile } from '@/components/UserProfile'; import { fetchUser } from '@/api/users';
jest.mock('@/api/users'); const mockFetchUser = fetchUser as jest.MockedFunction<typeof fetchUser>;
describe('UserProfile', () => { it('displays user name after loading', async () => { mockFetchUser.mockResolvedValue({ name: 'Alice', id: '1' }); render(<UserProfile userId="1" />); await waitFor(() => { expect(screen.getByText('Alice')).toBeInTheDocument(); }); expect(mockFetchUser).toHaveBeenCalledWith('1'); }); }); // AFTER: Vitest version import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { UserProfile } from '@/components/UserProfile'; import { fetchUser } from '@/api/users';
vi.mock('@/api/users'); const mockFetchUser = vi.mocked(fetchUser);
describe('UserProfile', () => { it('displays user name after loading', async () => { mockFetchUser.mockResolvedValue({ name: 'Alice', id: '1' }); render(<UserProfile userId="1" />); await waitFor(() => { expect(screen.getByText('Alice')).toBeInTheDocument(); }); expect(mockFetchUser).toHaveBeenCalledWith('1'); }); }); فایلهای اسنپشات معمولاً به همین شکل کار میکنند. Vitest از یک فرمت اسنپشات سازگار استفاده میکند. اگر با تفاوتهای سریالسازی مواجه شدید (که نادر است، اما با سریالسازهای اسنپشات سفارشی امکانپذیر است)، مجموعه خود را یک بار با پرچم -u اجرا کنید تا آنها را بهروزرسانی کنید.
مرحله ۴: به مشکلات رسیدگی کنید
اینجا جایی است که اکثر راهنماهای مهاجرت متوقف میشوند و در واقع اکثر مهاجرتها متوقف میشوند. در اینجا مشکلاتی که با آنها مواجه شدیم آمده است:
محدودهبندی کارخانهای vi.mock() . این رایجترین مشکل است. در Jest، توابع کارخانهای jest.mock() بالاتر از importها قرار میگیرند و متغیرهایی که خارج از factory تعریف میشوند، قابل دسترسی هستند. در Vitest، vi.mock() نیز قرار میگیرد، اما تابع factory محدودهبندی سختگیرانهتری دارد. متغیرهایی که به ماژولهای import شده ارجاع میدهند، در داخل factory در دسترس نیستند، مگر اینکه از vi.hoisted() استفاده کنید:
// THIS BREAKS in Vitest — `someHelper` is not available in the hoisted factory import { someHelper } from './helpers'; vi.mock('./myModule', () => ({ doThing: () => someHelper() // ReferenceError! }));
// FIX: use vi.hoisted() to define values available in the factory const { mockHelper } = vi.hoisted(() => ({ mockHelper: vi.fn(() => 'mocked') })); vi.mock('./myModule', () => ({ doThing: () => mockHelper() }));اینجا جایی است که بیشتر راهنماهای مهاجرت متوقف میشوند و در واقع اکثر مهاجرتها متوقف میشوند.
تایمر تقلبی. vi.useFakeTimers() مشابه jest.useFakeTimers() عمل میکند، اما رفتار پیشفرض آن تفاوتهای ظریفی دارد. تایمرهای تقلبی مدرن Vitest و Jest هر دو به طور پیشفرض تمام APIهای تایمر (از جمله Date ) را تقلبی میکنند. مشکل اصلی که با آن مواجه شدیم این بود که vi.advanceTimersByTime() در موارد خاص، تعامل متفاوتی با process.nextTick دارد. Vitest در زیر بدنه خود از @sinonjs/fake-timers استفاده میکند که رفتارهای خاص خود را در مورد زمانبندی ریزوظایف دارد. اگر تستهای پیچیده وابسته به تایمر دارید، این موارد را با دقت آزمایش کنید.
ماکتهای دستی (دایرکتوری __mocks__ ). Vitest از دایرکتوریهای __mocks__ پشتیبانی میکند، اما تفکیک مسیر از resolver وایت به جای Jest پیروی میکند. اگر ماکتهای دستی شما به الگوریتم تفکیک خاص Jest متکی هستند (بهویژه برای ماکتهای node_modules )، بررسی کنید که آیا به درستی انتخاب میشوند یا خیر. همچنین توجه داشته باشید که در Vitest، ماکتسازی خودکار node_modules نیاز به فراخوانی صریح vi.mock('module-name') دارد. صرفاً قرار دادن یک فایل در __mocks__ به خودی خود کافی نیست.
تطبیقدهندههای jest-dom . پکیج @testing-library/jest-dom با Vitest کار میکند، اما فایل راهاندازی شما باید آن را به درستی وارد کند:
// src/setupTests.ts import '@testing-library/jest-dom/vitest'; به زیرمسیر /vitest توجه کنید. این تضمین میکند که تطبیقدهندهها به جای Jest، با expect مربوط به Vitest ثبت میشوند. این زیرمسیر در @testing-library/jest-dom نسخه ۶.x اضافه شده است؛ مطمئن شوید که از نسخه اخیر استفاده میکنید.
مرحله 5: بهروزرسانی پیکربندی CI
# BEFORE: GitHub Actions with Jest - name: Run tests run: npx jest --maxWorkers=4 --coverage --ci - name: Upload coverage uses: actions/upload-artifact@v4 with: name: coverage path: coverage/lcov.info
# AFTER: GitHub Actions with Vitest - name: Run tests run: npx vitest run --coverage --reporter=verbose - name: Upload coverage uses: actions/upload-artifact@v4 with: name: coverage path: coverage/lcov.info اگر reporter: ['lcov'] مسیر خروجی پوشش به طور پیشفرض یکسان است. تعداد کارگران در CI معمولاً به طور خودکار توسط Vitest بر اساس CPU های موجود مدیریت میشود، اما در صورت نیاز میتوانید آن را به طور صریح با --poolOptions.threads.maxThreads تنظیم کنید.
چک لیست مهاجرت
این لیست را در ردیاب پروژه خود کپی کنید و به ترتیب روی آن کار کنید:
☐ vitest ، @vitest/coverage-v8 و jsdom (در صورت استفاده از محیط jsdom) را نصب کنید.
☐ حذف همه وابستگیهای مربوط به Jest ( jest ، ts-jest ، @swc/jest ، babel-jest ، @types/jest ، jest-environment-jsdom ، identity-obj-proxy )
☐ ایجاد vitest.config.ts یا اضافه کردن بلوک test به vite.config.ts موجود
☐ نگاشت ورودیهای moduleNameMapper به resolve.alias
☐ نگاشت setupFilesAfterEnv به test.setupFiles
☐ نگاشت coverageThreshold برای test.coverage.thresholds
☐ یافتن و جایگزینی سراسری: jest.fn/mock/spyOn/mocked به vi.fn/mock/spyOn/mocked
☐ import { describe, it, expect, vi } from 'vitest' را به هر فایل تست اضافه کنید، یا enable globals: true و "types": ["vitest/globals"] را به tsconfig.json اضافه کنید.
☐ بهروزرسانی تنظیمات @testing-library/jest-dom برای استفاده از زیرمسیر /vitest
☐ اجرای مجموعه کامل و رفع مشکلات مربوط به تعیین محدوده کارخانه vi.mock() با استفاده از vi.hoisted()
☐ بررسی کنید که آیا mock های دستی در دایرکتوریهای __mocks__ به درستی اجرا میشوند یا خیر.
☐ بهروزرسانی گردش کار CI با YAML (دستور تست، مسیرهای پوشش)
☐ در صورت نیاز، فایلهای اسنپشات را بهروزرسانی کنید (با پرچم -u اجرا کنید)
☐ اگر ابزارهای دیگر به babel.config.js یا ts-jest config نیازی ندارند، آنها را حذف کنید.
☐ فهرستهای کش Jest را حذف کنید ( .jest-cache ، jest_rs در صورت وجود)
☐ اجرای بررسی پوشش برای تأیید برابری پیکربندی آستانه
☐ CI سریعتر را جشن بگیرید
چارچوب تصمیمگیری: آیا واقعاً باید مهاجرت کنید؟
همین حالا مهاجرت کنید اگر...
شما در حال حاضر از Vite برای توسعه یا ساخت استفاده میکنید. این قویترین نشانه است. vite.config.ts شما از قبل نامهای مستعار، افزونهها و تبدیلهایی را که Vitest دوباره استفاده خواهد کرد، تعریف میکند. هزینه مهاجرت حداقل است و بهبود عملکرد Vitest بلافاصله قابل مشاهده است.
تیم شما زمان زیادی را صرف مقابله با پیکربندی Jest مربوط به ESM میکند. اگر transformIgnorePatterns ، extensionsToTreatAsEsm و moduleNameMapper منابع تکراری مشکل باشند، Vitest کل این دسته از مشکلات را از بین میبرد.
هزینههای CI نگرانکننده است و مجموعه شما به اندازهای بزرگ است که اختلاف سرعت به پول واقعی یا زمان انتظار واقعی تبدیل میشود. برای تیمی متشکل از 10 توسعهدهنده که هر کدام چندین بار در روز منتظر یک مجموعه تست 12 دقیقهای هستند، کاهش این زمان به کمتر از 4 دقیقه اهمیت دارد.
شما در حال شروع یک پروژه جدید هستید. هیچ هزینه مهاجرتی وجود ندارد و Vitest ابزار تست پیشفرض برای اکثر فریمورکهای مدرن است.
اگر ... روی شوخی بمانید
مجموعه شما پایدار و به اندازه کافی سریع است و هیچ کس در تیم با مشکلات پیکربندی سر و کار ندارد. هزینه مهاجرت صفر نیست و «خوب کار میکند» دلیل موجهی برای رها کردن همه چیز است.
شما به افزونههای مخصوص Jest یا اجراکنندههای سفارشی متکی هستید که معادل Vitest ندارند. قبل از شروع، بررسی کنید.
شما از Next.js به همراه Webpack استفاده میکنید. Jest هنوز گزینهی پیشفرض و بهترین گزینهی تست پشتیبانیشده برای این ترکیب است. این ممکن است با تکامل اکوسیستم Next.js تغییر کند، اما در حال حاضر، اگر پروژهی Next.js شما از Vite استفاده نمیکند، قبل از اقدام، هزینهی تغییر را با دقت بسنجید.
سازمان شما سرمایهگذاری زیادی روی زیرساختهای سفارشی Jest (گزارشگران سفارشی، لایههای تنظیم تست یا یکپارچهسازی ابزارهای سازمانی) انجام داده است که در آن هزینه تغییر از مزیت عملکرد بیشتر است.
رویکرد ترکیبی
لازم نیست همه چیز را یکجا منتقل کنید. وقتی در شرکت قبلیام یک monorepo با ۶ بسته و حدود ۳۰،۰۰۰ تست را منتقل کردم، با کوچکترین بسته (یک کتابخانه ابزارهای مشترک با ۸۰۰ تست) شروع کردم. مهاجرت ۲ ساعت طول کشید و مجموعه تست از ۴۵ ثانیه به ۸ ثانیه رسید. این به ما دادههای مشخصی داد تا مهاجرت بستههای باقیمانده را در اسپرینت بعدی توجیه کنیم.
ویژگی فضای کاری Vitest از اجرای بستههای مختلف با پیکربندیهای متفاوت پشتیبانی میکند، بنابراین میتوانید برخی از بستهها را در Vitest داشته باشید در حالی که برخی دیگر همچنان از Jest در طول انتقال استفاده میکنند. CI خود را طوری تنظیم کنید که هر دو دستور تست را تا زمان تکمیل مهاجرت اجرا کند.
مسیر مشخص است
در این مقایسه Vitest در مقابل Jest 2026، Vitest هم از نظر عملکرد و هم از نظر تجربه توسعهدهندگان برنده است. دادههای بنچمارک از مجموعه تولید ۵۰،۰۰۰ تستی ما، بسته به سناریو، بهبودهایی از ۳.۳ برابر (خط لوله CI) تا ۲۸ برابر (حالت watch) را نشان میدهد. میزان استفاده از حافظه بیش از نصف کاهش یافته است. پیچیدگی پیکربندی حتی بیشتر کاهش یافته است.
Jest همچنان یک چارچوب تست عملکردی و مستحکم است. هنوز از کار نیفتاده است. اما دیگر توصیه پیشفرض برای پروژههای جدید نیست و با حرکت اکوسیستم به سمت ابزارهای بومی ESM و مبتنی بر Vite، این شکاف بیشتر هم خواهد شد.
مسیر مهاجرت به خوبی تعریف شده است و موانع محدود هستند. با یک بسته در مونوریپوی خود شروع کنید. معیارها را با کدبیس خود مقایسه کنید. از چک لیست بالا برای پیگیری پیشرفت خود استفاده کنید. دادهها به شما کمک میکنند.
منابع لازم برای مهاجرت شما:



ارسال نظر