The Native Node Test Runner is Great

TL;DR: There’s a native test runner in node. It’s great. You should use it on your next project.

Things like this just feel good. Any time you can switch from using an external dependency to something built-in feels like taking off heavy winter boots. If you haven’t used it yet, node’s native test runner in node is excellent. It’s available in node verions 16.17+, 18+, and 20+. I’ve been using it on a couple small projects recently and so far it’s done everything I need a test runner to do and just feels right.

I set up a demo repo to show how I’m using it. There’s not much there, and that’s the point. I have two scripts in package.json:

"test": "node --test",
"test:watch": "node --test --watch ."

In add.js I have a single function that requires two arguments.

export default function add(a, b) {
  if (!a || !b) {
    throw Error("The add function requires 2 arguments");
  }

  return a + b;
}

Then in add.test.js I import a few native modules and write tests like normal:

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

describe("The add function", () => {
  it("#add throws without two arguments", () => {
    assert.throws(() => {
      add();
    }, Error);

    assert.throws(() => {
      add(2);
    }, Error);
  });

  it("#add returns the expected result", () => {
    assert.equal(add(5, 4), 9);
  });
});

I use the strict assert. Which is === instead of ==. That’s not a requirement, just a preference.

For one off tests and for a CI, I run npm test. When I’m working locally I keep npm test:watch running. The docs for the runner and for assert are clutch.

This just feels light and correct and I think it’s great. That’s all.