{"id":122848,"date":"2026-07-27T09:25:50","date_gmt":"2026-07-27T03:55:50","guid":{"rendered":"https:\/\/www.guvi.in\/blog\/?p=122848"},"modified":"2026-07-27T09:25:52","modified_gmt":"2026-07-27T03:55:52","slug":"vitest-unit-testing-tutorial","status":"publish","type":"post","link":"https:\/\/www.guvi.in\/blog\/vitest-unit-testing-tutorial\/","title":{"rendered":"Vitest Unit Testing Tutorial: A Complete 2026 Guide"},"content":{"rendered":"\n<p>Vitest unit testing framework is built on top of Vite that lets JavaScript and TypeScript projects run tests using the same fast build pipeline as their dev server. Install it with npm install -D vitest, write tests using describe, it, and expect (the same syntax Jest uses), and run them with npx vitest. Because it reuses Vite&#8217;s transform engine, test startup and re-runs are typically much faster than traditional Jest setups, especially in projects already using Vite.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>TL;DR Summary<\/strong><\/h2>\n\n\n\n<ul>\n<li>Vitest is a Vite-native test runner built for speed, with a Jest-compatible API so most teams migrate in hours, not weeks.<\/li>\n\n\n\n<li>It runs natively on ESM and TypeScript without extra transpilation config, which is where most Jest setups lose time.<\/li>\n\n\n\n<li>You get watch mode, a browser-based UI (vitest &#8211;ui), snapshot testing, and built-in mocking via the vi object.<\/li>\n\n\n\n<li>Coverage reporting works out of the box through v8 or istanbul providers \u2014 no separate babel plugin needed.<\/li>\n\n\n\n<li>This guide walks through installation, your first test, mocking, coverage, and how Vitest compares to Jest.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What Is Vitest?<\/strong><\/h2>\n\n\n\n<p>Vitest is an open-source unit testing framework created by the Vite team (led by Anthony Fu and collaborators) to solve a specific frustration: most JavaScript projects in 2026 build with Vite, but test with Jest a tool that uses a completely different transform pipeline underneath.<\/p>\n\n\n\n<p>That mismatch means double config. You set up Babel or ts-jest for Jest, separately from whatever Vite is already doing for your app code. Vitest collapses that into one pipeline. It reads your existing vite.config.ts, reuses Vite&#8217;s plugins, and runs your tests through the same engine that serves your dev server.<\/p>\n\n\n\n<p><em><strong>Pro Tip:<\/strong> If your project already has a vite.config.ts, you can usually add a test block to that same file instead of creating a separate Vitest config \u2014 one less file to maintain.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Why Developers Are Switching to Vitest<\/strong><\/h2>\n\n\n\n<p>A few reasons keep coming up when teams move off Jest:<\/p>\n\n\n\n<ul>\n<li><strong>Native ESM support.<\/strong> No babel-jest transform step for ES modules.<\/li>\n\n\n\n<li><strong>TypeScript out of the box.<\/strong> No ts-jest setup, no separate tsconfig for tests.<\/li>\n\n\n\n<li><strong>Jest-compatible API.<\/strong> describe, it, test, expect, beforeEach \u2014 the syntax most developers already know transfers directly.<\/li>\n\n\n\n<li><strong>Built-in UI mode.<\/strong> Running vitest &#8211;ui opens a browser dashboard showing test results, file trees, and console output, which is genuinely useful when debugging a flaky test rather than reading raw terminal output.<\/li>\n\n\n\n<li><strong>Watch mode by default.<\/strong> Tests re-run automatically and only re-execute files affected by your latest change.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Setting Up Vitest in a Project<\/strong><\/h2>\n\n\n\n<p>Here&#8217;s the basic install and config flow for a typical Vite-based project.<\/p>\n\n\n\n<p><strong>Step 1 \u2014 Install Vitest<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install -D vitest<\/code><\/pre>\n\n\n\n<p><strong>Step 2 \u2014 Add a test script<\/strong><\/p>\n\n\n\n<p>In package.json:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\"scripts\": {\n\n&nbsp;&nbsp;\"test\": \"vitest\"\n\n}<\/code><\/pre>\n\n\n\n<p><strong>Step 3 \u2014 Configure (optional but recommended)<\/strong><\/p>\n\n\n\n<p>If you don&#8217;t already have a vite.config.ts, create vitest.config.ts:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { defineConfig } from 'vitest\/config'\n\nexport default defineConfig({\n\n&nbsp;&nbsp;test: {\n\n&nbsp;&nbsp;&nbsp;&nbsp;globals: true,\n\n&nbsp;&nbsp;&nbsp;&nbsp;environment: 'jsdom', \/\/ use 'node' for backend-only projects\n\n&nbsp;&nbsp;},\n\n})<\/code><\/pre>\n\n\n\n<p>Setting globals: true means you don&#8217;t need to import describe, it, and expect in every file \u2014 they&#8217;re available globally, similar to Jest&#8217;s default behavior.<\/p>\n\n\n\n<p><em><strong>Warning:<\/strong> If you&#8217;re testing browser-facing code (DOM manipulation, React\/Vue components), you need environment: &#8216;jsdom&#8217; or environment: &#8216;happy-dom&#8217;. Without it, Vitest runs in a plain Node environment with no document or window object, and DOM-related tests will fail immediately.<\/em><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Writing Your First Test<\/h3>\n\n\n\n<p>Say you have a simple utility function:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ sum.ts\n\nexport function sum(a: number, b: number): number {\n\n&nbsp;&nbsp;return a + b\n\n}<\/code><\/pre>\n\n\n\n<p>Your test file sits alongside it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ sum.test.ts\n\nimport { describe, it, expect } from 'vitest'\n\nimport { sum } from '.\/sum'\n\ndescribe('sum()', () =&gt; {\n\n&nbsp;&nbsp;it('adds two positive numbers', () =&gt; {\n\n&nbsp;&nbsp;&nbsp;&nbsp;expect(sum(2, 3)).toBe(5)\n\n&nbsp;&nbsp;})\n\n&nbsp;&nbsp;it('handles negative numbers', () =&gt; {\n\n&nbsp;&nbsp;&nbsp;&nbsp;expect(sum(-1, -1)).toBe(-2)\n\n&nbsp;&nbsp;})\n\n})<\/code><\/pre>\n\n\n\n<p>Run it:<\/p>\n\n\n\n<p>npx vitest run<\/p>\n\n\n\n<p>Or stay in watch mode during development:<\/p>\n\n\n\n<p>npx vitest<\/p>\n\n\n\n<p>Vitest will re-run sum.test.ts automatically every time sum.ts or the test file changes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Mocking Functions and Modules<\/strong><\/h2>\n\n\n\n<p>Vitest ships its own mocking utilities through the vi object, replacing Jest&#8217;s jest.fn() and jest.mock().<\/p>\n\n\n\n<p><strong>Mocking a function:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { vi, it, expect } from 'vitest'\n\nconst mockFn = vi.fn().mockReturnValue(42)\n\nit('returns the mocked value', () =&gt; {\n\n&nbsp;&nbsp;expect(mockFn()).toBe(42)\n\n})<\/code><\/pre>\n\n\n\n<p><strong>Mocking a whole module:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { vi } from 'vitest'\n\nvi.mock('.\/api', () =&gt; ({\n\n&nbsp;&nbsp;fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Test User' }),\n\n}))<\/code><\/pre>\n\n\n\n<p><strong>Spying on an existing method:<\/strong><\/p>\n\n\n\n<p>const spy = vi.spyOn(console, &#8216;log&#8217;)<\/p>\n\n\n\n<p>console.log(&#8216;hello&#8217;)<\/p>\n\n\n\n<p>expect(spy).toHaveBeenCalledWith(&#8216;hello&#8217;)<\/p>\n\n\n\n<p>This is one of the few areas where teams migrating from Jest hit small syntax differences \u2014 the concepts map closely, but vi replaces jest as the namespace everywhere.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Measuring Test Coverage<\/h3>\n\n\n\n<p>Vitest has coverage built in through two providers: v8 (default, faster, uses Node&#8217;s native coverage) and istanbul (more configurable, slightly slower).<\/p>\n\n\n\n<p>Install the provider:<\/p>\n\n\n\n<p>npm install -D @vitest\/coverage-v8<\/p>\n\n\n\n<p>Run with coverage:<\/p>\n\n\n\n<p>npx vitest run &#8211;coverage<\/p>\n\n\n\n<p>This generates a terminal summary plus an HTML report (typically in a coverage\/ folder) showing line, branch, function, and statement coverage per file.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Vitest vs Jest: A Quick Comparison<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Feature<\/strong><\/th><th><strong>Vitest<\/strong><\/th><th><strong>Jest<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Build engine<\/td><td>Vite (esbuild\/Rollup)<\/td><td>Babel \/ ts-jest<\/td><\/tr><tr><td>Native ESM<\/td><td>Yes<\/td><td>Partial, needs config<\/td><\/tr><tr><td>Native TypeScript<\/td><td>Yes<\/td><td>Needs ts-jest or Babel preset<\/td><\/tr><tr><td>API syntax<\/td><td>Jest-compatible<\/td><td>Original<\/td><\/tr><tr><td>Watch mode<\/td><td>Default, fast HMR-style reruns<\/td><td>Available, generally slower startup<\/td><\/tr><tr><td>UI dashboard<\/td><td>Built-in (vitest &#8211;ui)<\/td><td>Requires third-party tools<\/td><\/tr><tr><td>Best fit<\/td><td>Vite-based projects (React, Vue, Svelte via Vite)<\/td><td>Projects already deep in the Jest\/Babel ecosystem, or non-Vite setups<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Jest remains a solid, mature choice \u2014 especially for teams not using Vite, or with large existing Jest suites where migration cost outweighs the speed gain. Vitest&#8217;s advantage is sharpest specifically when your app already builds with Vite.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Key Takeaways<\/h3>\n\n\n\n<ul>\n<li>Vitest reuses your Vite config, so there&#8217;s no separate transform pipeline to maintain alongside your app&#8217;s build setup.<\/li>\n\n\n\n<li>The describe\/it\/expect API is close enough to Jest that most existing test files need minimal changes to run.<\/li>\n\n\n\n<li>vi.fn(), vi.mock(), and vi.spyOn() cover the same mocking needs as Jest&#8217;s jest.* utilities.<\/li>\n\n\n\n<li>Coverage works out of the box via @vitest\/coverage-v8, with no Babel plugin required.<\/li>\n\n\n\n<li>Vitest makes the most sense for Vite-based projects; teams on other build tools should weigh migration cost against the speed benefit.<\/li>\n<\/ul>\n\n\n\n<p><em>If you want a structured, mentor-supported path through everything in a roadmap, HCL GUVI\u2019s IIT-M Pravartak Certified <a href=\"https:\/\/www.guvi.in\/zen-class\/full-stack-development-course\/?utm_source=blog&amp;utm_medium=hyperlink+&amp;utm_campaign=vitest-unit-testing-tutorial\" target=\"_blank\" data-type=\"link\" data-id=\"https:\/\/www.guvi.in\/zen-class\/full-stack-development-course\/?utm_source=blog&amp;utm_medium=hyperlink+&amp;utm_campaign=vitest-unit-testing-tutorial\" rel=\"noreferrer noopener\">Full Stack Developer Course<\/a> with AI Integration covers the entire journey, from HTML to deployment, with real projects, live sessions, and placement support. Over 10,000 students have used it to break into product-based companies.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h2>\n\n\n\n<p>Vitest isn&#8217;t a reinvention of unit testing \u2014 it&#8217;s a faster, more native fit for projects already built on Vite, with an API close enough to Jest that the learning curve is shallow. Whether you should switch your existing test suite depends mostly on your current build tool, but if you&#8217;re starting a new project on Vite, there&#8217;s little reason to reach for anything else.<\/p>\n\n\n\n<p>Ready to add Vitest to your own project? Start with the official <a href=\"https:\/\/vitest.dev\/guide\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Vitest Getting Started guide<\/a> and run your first test today.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>FAQs <\/strong><\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list \">\n<div id=\"faq-question-1783910617007\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q1: Is Vitest a replacement for Jest?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p> Vitest can replace Jest in most projects, especially those already using Vite. It isn&#8217;t a strict requirement, but it removes the need for separate Babel\/ts-jest configuration.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1783910621961\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q2: Does Vitest work with React and Vue?<\/strong> <\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Yes. Vitest works with any framework Vite supports, including React, Vue, Svelte, and Solid, as long as you set the correct environment (like jsdom) for DOM testing.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1783910629437\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q3: Can I use Jest matchers in Vitest?<\/strong> <\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Yes. Vitest&#8217;s expect API is built to be Jest-compatible, so most common matchers like toBe, toEqual, and toHaveBeenCalledWith work without changes.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1783910639630\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q4: Do I need a separate config file for Vitest?<\/strong> <\/h3>\n<div class=\"rank-math-answer \">\n\n<p>No, if you already have a vite.config.ts. You can add a test property directly to that existing config instead of creating a new file.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1783910649525\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q5: How do I check test coverage in Vitest?<\/strong> <\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Install @vitest\/coverage-v8, then run npx vitest run &#8211;coverage. This generates both a terminal summary and an HTML report.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1783910656869\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q6: Is Vitest slower or faster than Jest?<\/strong> <\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Vitest is generally faster in Vite-based projects because it reuses Vite&#8217;s existing transform pipeline instead of running a separate Babel-based one, though actual gains vary by project size and configuration.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Vitest unit testing framework is built on top of Vite that lets JavaScript and TypeScript projects run tests using the same fast build pipeline as their dev server. Install it with npm install -D vitest, write tests using describe, it, and expect (the same syntax Jest uses), and run them with npx vitest. Because it [&hellip;]<\/p>\n","protected":false},"author":63,"featured_media":126910,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[294],"tags":[],"views":"24","authorinfo":{"name":"Vishalini Devarajan","url":"https:\/\/www.guvi.in\/blog\/author\/vishalini\/"},"thumbnailURL":"https:\/\/www.guvi.in\/blog\/wp-content\/uploads\/2026\/07\/Vitest-Unit-Testing-300x116.webp","_links":{"self":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/122848"}],"collection":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/users\/63"}],"replies":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/comments?post=122848"}],"version-history":[{"count":6,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/122848\/revisions"}],"predecessor-version":[{"id":126912,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/122848\/revisions\/126912"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media\/126910"}],"wp:attachment":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media?parent=122848"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/categories?post=122848"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/tags?post=122848"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}