Smart tech coverage, no jargon: Interestingdevelopment

JavaScript Unit Testing: A Practical 2026 Guide To Faster, Safer Code

javascript unit testing
Decision What the documentation supports Planning use
Adopting Vitest Requires Vite >=6.4.0 and Node >=22.12.0 Check the project’s Vite and Node versions before choosing it. An older toolchain means upgrading or picking another runner.
Naming test files By default, a test file name must contain .test. or .spec. Name files to match so the runner finds them, for example shout.test.js.
Writing each test Each test must include one or more assertions, usually a single one Give every test a clear expected value. A test that asserts nothing proves nothing.
Starting new code CFPB recommends test-driven development for new JavaScript: write only enough of a unit test to fail Write the failing test before the function it describes.
Running in automation vitest run executes tests once without watching for file changes Use it in CI steps, where nothing should wait for file changes.

Conventions above come from the Vitest guide and the CFPB JavaScript unit tests documentation.

JavaScript unit testing means writing small automated checks that call your code and assert on the result. Pick a runner such as Vitest, Jest or Mocha, write a failing test, make it pass, then run the suite in CI.

For where testing sits among the rest of engineering practice, see our software development guide. The sections below move from what tests change in a project, to choosing a runner, to a first test, to the pipeline.

What unit tests change for a project

Unit tests make a growing codebase safer to change, and the trigger for needing them is complexity. MDN states that any codebase past a certain level of complexity needs to have a system of tests associated with it.

  • Complexity is the trigger. A throwaway script can live without a suite. Once a codebase has grown past that level, tests stop being optional.
  • Automation replaces repetition. MDN observes that manually running tests on several browsers and devices, several times per day, gets tedious. That is the practical case for scripting checks and running them through CI/CD (continuous integration and delivery) rather than by hand.
  • Unit testing is core practice. Pluralsight’s course on unit tests with Jest describes unit testing as a vital part of software development.
  • Test-first is a team policy you can adopt. CFPB recommends test-driven development (TDD) when coding new JavaScript, so a team can make failing-test-first the default for new functions.

A suite also gives reviewers something concrete to read alongside a change, and the What Is Code Review? A Practical Guide For Developers (2026) – Interestingdevelopment piece covers how that review works. Unit tests cover the small pieces; how those pieces fit together at scale is the subject of the System Design Primer: Build Scalable, Reliable Systems Fast — A Practical 2026 Guide – Interestingdevelopment.

Choosing a test runner

Choose the runner your toolchain already points to: Vitest for Vite projects that meet its version requirements, Jest when you want a widely used runner with plenty of learning material, and Mocha when you want to assemble your own stack or test in browsers.

  • Vitest. The Vitest guide describes it as a next generation testing framework powered by Vite. It needs Vite >=6.4.0 and Node >=22.12.0, and it reads your vite.config.* by default, so settings from an existing Vite project carry over. CFPB says Vitest is the framework it uses for writing and running its JavaScript unit tests.
  • Jest. A developer comparison calls Jest and Mocha both highly popular choices among developers. Jest suits teams who value that popularity, and structured training exists for writing JavaScript unit tests with it, including a module on measuring code coverage.
  • Mocha. The same comparison notes that Mocha is not limited to Node.js and also supports browser testing. It suits projects that need tests to run in a browser or that prefer to mix and match components rather than adopt one bundled framework.

Do not switch runners for its own sake. If the project already has a working suite in Jest or Mocha, keep it and spend the effort on writing tests. For a new Vite project, Vitest is the shortest path to a first test, and the walkthrough below uses it because its guide documents each step.

Writing your first test with Vitest

A first JavaScript unit test needs three things: a runner installed, a test file whose name contains .test. or .spec., and an assertion on a function’s output. The Vitest guide’s own example verifies a function that adds two numbers. The sample below uses a small text helper instead, since the pattern is the same for any pure function.

The steps

  1. Install the runner. Run npm install -D vitest in the project.
  2. Write the test first. Create shout.test.js. The name contains .test., so the default pattern matches it.

    import { expect, test } from ‘vitest’
    import { shout } from ‘./shout.js’

    test(‘upper-cases the text and adds an exclamation mark’, () => {
      expect(shout(‘hello’)).toBe(‘HELLO!’)
    })

    It fails at this point because shout does not exist yet, which is exactly the state CFPB’s TDD guidance asks you to reach before writing code. CFPB also notes that individual tests are encapsulated by it methods, an alias of Vitest’s test method, so you may write it instead of test.

  3. Write just enough code. Create shout.js.

    export function shout(text) {
      return text.toUpperCase() + ‘!’
    }

  4. Add a script and run it. Put “test”: “vitest” in the scripts section of package.json, then run npm run test, yarn test or pnpm test.
  5. Read the result. A passing run reports the test files and tests as passed. Once it is green, refactor with the test as a safety net.

Naming and organising test files

Stick to the default pattern unless the team deliberately changes it. Vitest’s guide says that by default tests must contain .test. or .spec. in their file name. CFPB’s convention is stricter about what the name says: test file names should match what they are testing, with a -spec suffix added. If you adopt a convention like that, confirm that the runner still discovers the files, because the default pattern is only the starting point.

Running tests in CI/CD

Run vitest run in the pipeline, because the Vitest guide specifies it for running tests once without watching for file changes. A CI step that launches a watcher has no reason to end, so the one-shot command is the safe choice for automation.

The screen shows several terminal-style panels of text; individual lines are not legible at this size.
  • Match the runtime. Vitest requires Vite >=6.4.0 and Node >=22.12.0, so the CI image must provide a Node version that meets it. A suite that passes locally can still fail in a pipeline pinned to an older Node.
  • Call the same script everywhere. Point the pipeline at the package.json test script, or at a script that wraps vitest run, so developers and CI exercise the suite the same way.
  • Let failures stop the build. Configure the step so a failing test run fails the job. A check that cannot fail does not protect the main branch.
  • Use the equivalent for other runners. Jest and Mocha projects need their own run-once commands in CI, so check the runner’s documentation before writing the pipeline file.

With a first test passing locally and the same command running in CI/CD, the workflow is complete. From here, add tests for each new function and bug fix, and keep the suite running on every change.

Frequently asked questions

How does Vitest find its configuration?

Vitest reads your vite.config.* file by default. You can add a vitest.config.* file if you want test-specific configuration.

Which testing styles does Mocha support?

Mocha offers BDD and TDD interfaces, so you can choose your preferred style. It also pairs with assertion and mocking libraries such as Chai and Sinon.

How do I run a single test file or a directory of tests?

CFPB’s documentation says to pass a test file name or a directory name to the test command. This keeps feedback quick while you work on one module.