The built-in test runner
An automated test is a small program that calls your code and checks the result. Tests are run after every change: if something breaks, you learn about it at once, not from the blog's visitors. Node.js has built-in modules for this — node:test (running tests) and node:assert (checks) — so nothing needs to be installed.
The first test
test() takes a name and a function with checks. assert.equal() compares the actual value with the expected one:
import { test } from 'node:test';
import assert from 'node:assert/strict';
function slugify(title) {
return title.toLowerCase().trim().replace(/\s+/g, '-');
}
test('replaces spaces with hyphens', () => {
assert.equal(slugify('Hello Node js'), 'hello-node-js');
});
test('trims spaces at the edges', () => {
assert.equal(slugify(' Blog '), 'blog');
});Run the file with node: the terminal shows a report with two check marks and the line pass 2. If a check fails, the report shows the expected and actual values, and the program returns code 1.
Code and tests in separate files
In a project the function lives in src/slugify.js:
// src/slugify.js
export function slugify(title) {
return title.toLowerCase().trim().replace(/\s+/g, '-');
}And the tests live in the test folder, in a file ending in .test.js:
// test/slugify.test.js
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { slugify } from '../src/slugify.js';
test('replaces spaces with hyphens', () => {
assert.equal(slugify('Hello Node js'), 'hello-node-js');
});Running all tests
The --test flag finds files named like *.test.js in all project folders and runs them:
node --testAdd it to package.json to run it with the familiar npm test:
{
"scripts": {
"test": "node --test"
}
}With --watch, tests rerun on every save: node --test --watch.
Checks
| Check | What it compares |
|---|---|
assert.equal(a, b) | Values, strictly with === |
assert.deepEqual(a, b) | Objects and arrays by content |
assert.ok(value) | That the value is truthy |
assert.throws(fn) | That the function throws |
await assert.rejects(promise) | That the promise rejects |
An asynchronous test
If the code is asynchronous, declare the test function async and await the result:
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { readFile, writeFile } from 'node:fs/promises';
test('posts are saved as JSON', async () => {
await writeFile('posts-test.json', JSON.stringify([{ id: 1, title: 'Hello' }]));
const posts = JSON.parse(await readFile('posts-test.json', 'utf8'));
assert.deepEqual(posts, [{ id: 1, title: 'Hello' }]);
});Common mistake
Two objects with the same content are different objects, so assert.equal() does not consider them equal:
import assert from 'node:assert/strict';
assert.deepEqual({ id: 1 }, { id: 1 }); // passes
console.log('deepEqual compares content');
// assert.equal({ id: 1 }, { id: 1 }) would failFor objects and arrays use assert.deepEqual().