Assertions and Structure

describe, it and node:assert/strict, arranged so that the failure line alone tells you what broke.

Syntaxassert.deepStrictEqual(actual, expected[, message])

An assertion is a statement about a value that must be true. If it is not, it throws an AssertionError; the runner marks that test failed and prints both values. Assertions come from node:assert/strict, which is built in.

Strict, not loose

node:assert/strict is the same module as node:assert with the loose functions pointed at the strict ones. assert.equal becomes assert.strictEqual and assert.deepEqual becomes assert.deepStrictEqual — the same function under a shorter name. Imported from plain node:assert, assert.equal compares with ==, so assert.equal('5', 5) passes and a bug ships. Import the strict form every time.

Use assert.strictEqual for a single value, assert.deepStrictEqual for objects and arrays, assert.partialDeepStrictEqual when only some fields of a response matter (added in Node.js 22.13.0, stable in Node.js 24), and assert.throws or assert.rejects when the correct behaviour is an error.

Structure

describe groups tests and it is one test. They are aliases: describe is suite and it is test, interchangeable since Node.js 20.13.0, with the same arguments and options. Both spellings appear in real projects. before, after, beforeEach and afterEach run setup and cleanup around them.

Inside a test: build the input, call the function once, then assert. One behaviour per test, named as a sentence, so a failure in CI is readable without opening the file.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Turn a bug report into one failing test that names the broken behaviour before you fix it.
  • Check a parser's whole output object in a single assertion instead of field by field.
  • Assert that invalid input is rejected with a specific error code, not just any error.

More examples

describe and it, with the longhand beside them

describe/it and suite/test produce identical output; which pair a project uses is a matter of habit. beforeEach rebuilds the fixture for every test, so one test cannot leave state behind that makes the next one pass or fail by accident.

Example · javascript
import { describe, it, suite, test, beforeEach } from 'node:test';
import assert from 'node:assert/strict';

describe('cart', () => {
  let cart;

  beforeEach(() => {
    cart = []; // runs before each it() in this group
  });

  it('starts empty', () => {
    assert.equal(cart.length, 0);
  });

  it('keeps items in the order they were added', () => {
    cart.push('a', 'b');
    assert.deepStrictEqual(cart, ['a', 'b']);
  });
});

// Exactly the same run, written with the names the aliases point at:
suite('cart', () => {
  test('starts empty', () => {
    assert.equal([].length, 0);
  });
});

Asserting that something throws

assert.throws takes a function, not a call: pass `() => withdraw(10, 50)`, because `withdraw(10, 50)` would throw before assert ever sees it. The object form checks fields on the error, which keeps the test passing when the message is reworded but the code stays.

Example · javascript
import { test } from 'node:test';
import assert from 'node:assert/strict';

function withdraw(balance, amount) {
  if (amount > balance) {
    const err = new Error('insufficient funds');
    err.code = 'FUNDS'; // a stable code, safe to assert on
    throw err;
  }
  return balance - amount;
}

test('refuses an overdraft', () => {
  // The second argument lists fields the thrown error must have.
  assert.throws(() => withdraw(10, 50), { code: 'FUNDS', message: 'insufficient funds' });
});

test('a normal withdrawal still works', () => {
  assert.equal(withdraw(50, 10), 40);
});

When only part of the response matters

partialDeepStrictEqual avoids the choice between deepStrictEqual against a whole object with a timestamp in it and a pile of single-field assertions. t.plan guards the count: if the test ends having run no assertions, it fails instead of passing quietly.

Example · javascript
import { test } from 'node:test';
import assert from 'node:assert/strict';

const response = {
  id: 7,
  name: 'ada',
  createdAt: '2026-01-04T10:00:00Z', // changes on every insert
  version: 3,
};

test('returns the user that was asked for', () => {
  // Compares only the keys present in the expected object.
  // Added in Node.js 22.13.0, stable in Node.js 24.
  assert.partialDeepStrictEqual(response, { id: 7, name: 'ada' });
});

test('the test context carries the same assertions', (t) => {
  t.plan(1); // exactly one context assertion must run
  t.assert.equal(response.version, 3);
});

Discussion

  • Be the first to comment on this lesson.