assert.deepStrictEqual()

Recursively compares two values by structure, type and prototype, throwing when they differ.

Signature
assert.deepStrictEqual(actual, expected[, message])
  • Added in Node.js v1.2.0

Parameters

ParameterTypeDescription
$actualanyThe value produced by the code under test.
$expectedanyThe value it is compared against, recursively.
$messageoptionalstring | Error | FunctionError message, an Error instance to throw instead, or a function called on failure.

Errors

Throws an AssertionError with code ERR_ASSERTION when the two values differ, including a printed diff; if message is an Error instance, that error is thrown instead.

Changelog

  • PHP v25.1.0Message may now be a printf-like format string or function.
  • PHP v25.0.0Promises are no longer considered equal unless they are the same instance.
  • PHP v25.0.0Invalid dates are now considered equal.
  • PHP v24.0.0Recursion now stops when either side encounters a circular reference.
  • PHP v22.2.0, v20.15.0Error cause and errors properties are now compared as well.
  • PHP v18.0.0A regular expression's lastIndex property is now compared as well.
  • PHP v9.0.0Enumerable symbol properties are now compared, and NaN is compared using SameValueZero.
  • PHP v8.0.0Set and Map contents are also compared.

assert.deepStrictEqual() is the workhorse of Node test suites: it walks both values recursively and requires them to agree on structure, on primitive types, and on prototype. Reach for it whenever you are checking a parsed payload, a returned object, an array of records, or a Map/Set.

The rules that surprise people

  • Prototypes must match. A class instance never equals a plain object literal with the same fields, and a value from Object.create(null) never equals {}.
  • Only own enumerable properties count, plus enumerable symbol keys since Node 9. Getters on the prototype and non-enumerable properties are ignored.
  • An explicit undefined is a property. {a: 1, b: undefined} does not equal {a: 1}.
  • Array holes are not undefined. [, 1] does not equal [undefined, 1].
  • Primitives use Object.is(), except that NaN uses SameValueZero, so nested NaN values match.
  • Set and Map compare contents, and order does not matter.

Prefer it over JSON round-tripping

Comparing JSON.stringify() output is the common workaround people reach for; it silently drops undefined, functions and symbols, depends on key order, and turns Date objects into strings. deepStrictEqual handles all of those correctly and prints a readable diff on failure.

Example

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

When to use it

  • Assert a JSON parser produced exactly the expected object tree
  • Compare an array of database rows against a fixture in a test
  • Verify a Map of aggregated counts holds precisely the expected entries

Frequently asked questions

Why does assert.deepStrictEqual() fail on two objects that look identical?

Almost always a prototype or a type mismatch: a class instance never equals a plain object literal, and 7 never equals '7'. Read the printed diff — Node marks the differing lines with + and - and names the constructor on each side.

What is the difference between deepEqual and deepStrictEqual?

deepEqual compares primitives with == and ignores prototypes, so { a: 1 } equals { a: '1' }; deepStrictEqual uses Object.is() and requires matching prototypes. deepEqual is classified as Legacy and the docs point you to deepStrictEqual.

Does assert.deepStrictEqual() care about the order of object keys or Set entries?

No. Object key order is irrelevant, and Set and Map contents are compared without regard to insertion order. Array order does matter, because position is part of an array's structure.

More examples

Comparing a class instance against a fixture

The failing call shows the prototype check in action: the diff names User on one side and Object on the other, even though every own property matches. Spreading into a plain object or building the expected value with the same constructor both fix it; the prototype getter never participates either way.

Example · javascript
const assert = require('node:assert');

class User {
  constructor(id, name) {
    this.id = id;
    this.name = name;
  }
  get initials() {           // on the prototype — not compared
    return this.name[0].toUpperCase();
  }
}

const user = new User(1, 'ada');

try {
  assert.deepStrictEqual(user, { id: 1, name: 'ada' });
} catch (err) {
  console.log(err.message);
  // Expected values to be strictly deep-equal:
  // ... Object !== User ...
}

// Two ways to make the comparison work:
assert.deepStrictEqual({ ...user }, { id: 1, name: 'ada' });
assert.deepStrictEqual(user, new User(1, 'ada'));
console.log('both workarounds pass');

Why it beats JSON.stringify comparison

The first two console.log lines show the two ways string comparison lies: it is sensitive to key order and blind to undefined-valued keys. deepStrictEqual has the opposite behaviour on both counts, which is what you want from a test assertion.

Example · javascript
const assert = require('node:assert');

const a = { name: 'ada', deletedAt: undefined, born: new Date('1815-12-10') };
const b = { born: new Date('1815-12-10'), name: 'ada' };

// JSON round-tripping says these are the same...
console.log(JSON.stringify(a) === JSON.stringify(b)); // false — key order differs
console.log(JSON.stringify({ x: undefined }) === JSON.stringify({})); // true — key vanished

// deepStrictEqual is order-insensitive but undefined-aware.
try {
  assert.deepStrictEqual(a, b);
} catch (err) {
  console.log('deletedAt is a real difference:', err.code);
}

delete a.deletedAt;
assert.deepStrictEqual(a, b);
console.log('key order never mattered');

Inside the built-in test runner

One assertion covers the whole return value, including the nested arrays and the Map itself. If the parser dropped the decodeURIComponent call, the runner would print a diff pointing at 'hello%20world' versus 'hello world' rather than a bare false.

Example · javascript
const test = require('node:test');
const assert = require('node:assert/strict');

function parseQuery(qs) {
  const out = new Map();
  for (const pair of qs.split('&')) {
    const [k, v = ''] = pair.split('=');
    const list = out.get(k) ?? [];
    list.push(decodeURIComponent(v));
    out.set(k, list);
  }
  return out;
}

test('parseQuery groups repeated keys', () => {
  assert.deepStrictEqual(
    parseQuery('tag=a&tag=b&q=hello%20world'),
    new Map([['tag', ['a', 'b']], ['q', ['hello world']]]),
  );
});

// $ node --test
// ✓ parseQuery groups repeated keys (1.1ms)

Discussion

  • Be the first to comment on this lesson.