Testing JavaScript in Node
Introduction
Automated tests catch regressions before users do. Node ships node:test and node:assert; many projects add Vitest or Jest for richer DX. This chapter writes small unit tests for pure functions and async code—patterns that apply in front-end and back-end repos.
Prerequisites
Pure Function Under Test
math.mjs:
javascript
export function clamp(value, min, max) {
if (value < min) return min;
if (value > max) return max;
return value;
}node:test + node:assert
math.test.mjs:
javascript
import test from "node:test";
import assert from "node:assert/strict";
import { clamp } from "./math.mjs";
test("clamp lower bound", () => {
assert.equal(clamp(-5, 0, 100), 0);
});
test("clamp upper bound", () => {
assert.equal(clamp(150, 0, 100), 100);
});
test("clamp middle", () => {
assert.equal(clamp(42, 0, 100), 42);
});Run:
bash
node --test math.test.mjsAsync Test
javascript
import test from "node:test";
import assert from "node:assert/strict";
async function fetchStatus() {
const res = await fetch("https://example.com");
return res.status;
}
test("example.com returns 200", async () => {
const status = await fetchStatus();
assert.equal(status, 200);
});Network tests can be flaky—mock fetch in CI or mark as integration tests.
Vitest (Common in Vite Projects)
bash
npm install --save-dev vitestpackage.json:
json
{
"scripts": {
"test": "vitest run"
}
}math.test.mjs with Vitest-compatible syntax:
javascript
import { describe, it, expect } from "vitest";
import { clamp } from "./math.mjs";
describe("clamp", () => {
it("limits max", () => {
expect(clamp(200, 0, 100)).toBe(100);
});
});bash
npm testWhat to Test First
- Pure utilities (formatting, validation)
- API handlers with mocked dependencies
- Edge cases (
null, empty array, bad input)
UI tests often use Playwright or Cypress—separate from unit tests.
Arrange–Act–Assert
javascript
// pattern comment structure
test("discount applies", () => {
// arrange
const price = 100;
const rate = 0.1;
// act
const total = price * (1 - rate);
// assert
assert.equal(total, 90);
});Mini Example: Test Error Thrown
javascript
import assert from "node:assert/strict";
import test from "node:test";
function parsePort(text) {
const n = Number(text);
if (Number.isNaN(n)) throw new Error("invalid port");
return n;
}
test("invalid port throws", () => {
assert.throws(() => parsePort("abc"), /invalid port/);
});FAQ
How much coverage?
Aim for critical paths and bugs you have seen—not 100% line coverage as a goal itself.
TDD?
Optional—tests help most when requirements are clear.
CI?
Run npm test on every pull request in GitHub Actions or similar.