Testing foundations
Run the first Node test
Use the built-in Node.js test runner and strict assertions to execute one fast deterministic test without another framework.
Node.js ships with a test runner. You don’t need Jest or Vitest to start. It finds your test files, runs them, reports failures, and it also does filtering, mocking, and coverage.
I like starting here because there’s nothing to install and nothing to configure.
The first test
We’ll test a normalizeTitle() function from our Books API. It should trim the whitespace around a title. Create books.test.js next to books.js:
import test from 'node:test'
import assert from 'node:assert/strict'
import { normalizeTitle } from './books.js'
test('removes surrounding whitespace', () => {
assert.equal(normalizeTitle(' Dune '), 'Dune')
})
Notice the import from node:assert/strict. The strict version compares with ===. The loose version uses ==, and == can turn the number 1 and the string '1' into an apparent pass. I always use strict.
Run it with:
node --test
Node finds every file ending in .test.js and runs it. You get this:
✔ removes surrounding whitespace (0.8ms)
ℹ tests 1
ℹ pass 1
ℹ fail 0
Name the behavior
The test name should say what the code does, not which function it calls. normalizes title tells you nothing when it fails. removes surrounding whitespace but preserves internal spaces tells you which rule broke.
That name is part of the diagnostic. When a test fails, Node prints the name, the expected value, the actual value, and the line. Good names make that output enough to fix the bug.
Prove the test is connected
A test that can’t fail is worse than no test. So I do one check on every new test: break the code on purpose.
Change normalizeTitle() to return the input unchanged, and run again:
✖ removes surrounding whitespace (1.2ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
' Dune ' !== 'Dune'
Now restore the function and watch it pass again. This red-green check proves the assertion actually looks at the returned value. A test that asserts on the input string, or on a constant the function never produced, would stay green through the mutation.
Try this now: add a second test for 'The Left Hand of Darkness', with two spaces inside. Decide whether internal spaces are preserved or collapsed, put that decision in the name and the expectation, and run node --test. Then make one expectation wrong on purpose and read the diff before you fix it.
Lesson completed