Back to roadmaps nodejs Course

Testing with Node.js Native Test Runner

Starting with Node.js v20, the runtime includes a native test runner and assertion library. This means you can write and run tests without installing third-party frameworks like Jest or Mocha.


1. Writing a Basic Unit Test

The native test runner is available in the node:test module, and assertions can be imported from node:assert:

// math.js
export const add = (a, b) => a + b;

// math.test.js
import test from "node:test";
import assert from "node:assert/strict"; // Strict assertion mode is recommended
import { add } from "./math.js";

test("should add two positive integers correctly", () => {
  const result = add(2, 3);
  assert.equal(result, 5);
});

2. Running the Tests

To find and run all test files in your project, use the --test execution flag:

# Execute all files matching *.test.js or test/*.js
node --test

You can watch files for changes and automatically rerun tests using the watch option:

# Rerun tests on file changes
node --test --watch

3. Nesting Tests with describe and it

For larger projects, group related tests using describe block wrappers and it assertions:

import { describe, it } from "node:test";
import assert from "node:assert/strict";

describe("User Model validations", () => {
  it("should accept valid username values", () => {
    const user = { username: "nodeDev" };
    assert.ok(user.username.length >= 3);
  });

  it("should reject usernames that are too short", () => {
    const user = { username: "jo" };
    assert.throws(() => {
      if (user.username.length < 3) throw new Error("Invalid name");
    });
  });
});

4. Testing Asynchronous Functions

To test asynchronous operations, pass an async callback function to the test runner. The test runner will wait for all promises to resolve:

import test from "node:test";
import assert from "node:assert/strict";

const fetchUserData = async (id) => {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id, role: "admin" }), 100);
  });
};

test("should fetch asynchronous profile settings", async () => {
  const data = await fetchUserData(42);
  assert.deepEqual(data, { id: 42, role: "admin" });
});

5. Mocking Functions and Timers

The test runner includes built-in mocking helpers. You can track function calls, monitor argument parameters, and override response behaviors:

import test from "node:test";
import assert from "node:assert/strict";

test("should track method invocations using mock helper", (t) => {
  const calculator = {
    multiply: (a, b) => a * b
  };

  // Mock the multiply method
  const spy = t.mock.fn(calculator.multiply);

  const result = spy(4, 5);

  assert.equal(result, 20);
  assert.equal(spy.mock.calls.length, 1);
  assert.deepEqual(spy.mock.calls[0].arguments, [4, 5]);
});
Published on Last updated: