↓Skip to main content
  1. Testing & Quality/

Unit Testing JavaScript with Vitest

·407 words·2 mins·
JavaScript Vitest Testing Unit Testing
Ifarra
Author
Ifarra
Disturbing the peace!!
Table of Contents

A unit test gives one small piece of logic a clear input and checks its observable result. Vitest provides a test runner and assertion library that work well with modern JavaScript projects. The key habit is to test behavior at a stable boundary instead of reproducing the implementation line by line.

Arrange inputs, act once, and assert observable results in a Vitest unit test
A small test has a clear setup, action, and expectation.

Install and add a test command
#

Install Vitest in a project that already uses npm:

npm install --save-dev vitest

Add a script to package.json:

{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest"
  }
}

vitest run performs one run and exits, which is useful in CI. The watch command reruns related tests while you edit.

Arrange, act, assert
#

Extract logic that does not depend on the DOM or a live service into a function that can be tested directly:

export function formatDisplayName(user) {
  const first = user.firstName?.trim() ?? "";
  const last = user.lastName?.trim() ?? "";
  return [first, last].filter(Boolean).join(" ") || "Guest";
}

Then cover the behavior with a focused test:

import { describe, expect, it } from "vitest";
import { formatDisplayName } from "./formatDisplayName.js";

describe("formatDisplayName", () => {
  it("joins non-empty name parts", () => {
    expect(formatDisplayName({ firstName: " Ada ", lastName: "Lovelace" }))
      .toBe("Ada Lovelace");
  });

  it("uses Guest when no name is available", () => {
    expect(formatDisplayName({})).toBe("Guest");
  });
});

The setup creates a representative input, the action calls the function once, and the assertion checks what a caller can observe.

Cover meaningful boundaries
#

Add cases for empty values, optional fields, invalid input, and boundaries where behavior changes. Avoid writing a test for every possible string if many strings follow the same rule. A small set of representative cases makes the contract easy to read.

Keep tests independent
#

Each test should pass regardless of execution order. Create fresh objects in each test, avoid sharing mutable state, and do not rely on real time, random values, or network availability. For code that reads the current time, inject a clock or use Vitest’s fake timers for the narrow behavior that depends on time.

When not to mock
#

Pure functions do not need mocks. A mock is useful when a test must control or observe a dependency such as an email sender. Too many mocks can make tests pass while the real components no longer work together; cover those boundaries with integration tests as well.

Next: testing asynchronous fetch code and testing user-visible React behavior.

Related

Mocks, Stubs, and Fakes: Choosing Test Doubles
·350 words·2 mins
Testing Mocks Software Design Unit Testing
Compare stubs, mocks, fakes, and spies, then apply the smallest useful substitute in a test.
Testing Asynchronous JavaScript and Fetch Requests
·370 words·2 mins
JavaScript Fetch API Async Testing
Separate the network boundary from application logic and test each asynchronous outcome deterministically.
The Testing Pyramid: Unit, Integration, and End-to-End Tests
·1982 words·10 mins
Testing Software Quality Unit Testing Integration Testing
A practical guide to the testing pyramid, with examples of what belongs in unit, integration, and end-to-end tests.