It Should Help

Objects

should().objects(x, y) compares two objects deeply, field by field — with four modifiers that shape how fields are compared. The verifier takes both objects up front:

should().objects(actual, expected).equal();

On mismatch the failure names the field and both values: Objects have different 'name': "a" & "b". — for nested fields the path is included ('address.city').

The builder rules

The modifiers — rule, map, ignoring, compareOnly — configure the comparison and must be called before equal():

  1. equal() runs the comparison with whatever was configured before it.

  2. rule(field, checker) may be called any number of times — one custom rule per field.

  3. compareOnly(...fields) defines an exclusive whitelist — it overrides anything set by ignoring().

  4. map(fieldA, fieldB) compares a field of the first object against a differently named field of the second.

Ignoring fields

For generated or irrelevant data (timestamps, ids):

should() .objects({ id: 1, name: 'a', updatedAt: today }, { id: 1, name: 'a', updatedAt: yesterday }) .ignoring('updatedAt') .equal(); // ok

Comparing only some fields

The inverse — an exclusive list; everything else is skipped (and it cancels ignoring):

should() .objects({ id: 1, name: 'a', createdAt: d1, updatedAt: d2 }, { id: 1, name: 'a', createdAt: d1, updatedAt: d3 }) .compareOnly('id', 'name', 'createdAt') .equal(); // ok

Renaming fields across the two objects

When the sides name the same thing differently:

should() .objects({ id: 1, name: 'a' }, { id: 1, fullName: 'a' }) .map('name', 'fullName') .equal(); // ok

Custom rules per field

A rule receives both field values and decides equality itself — case-insensitivity, tolerance, anything:

should() .objects({ id: 1, name: 'a' }, { id: 1, name: 'A' }) .rule('name', (a, b) => a.toLowerCase() === b.toLowerCase()) .equal(); // ok — 'Objects failed custom rule for ...' on failure

How deep comparison works

  • Nested objects are compared recursively; the reported field path shows the route ('user.address.city').

  • Date properties are compared by their string representation — same instant passes, different instants fail regardless of accuracy settings (accuracy belongs to Dates).

  • The two objects must have the same number of properties (after map renames and ignoring/compareOnly exclusions) — otherwise the check fails with The objects has different number of properties.

  • Without modifiers, plain === applies per field — reference equality for nested objects, so deep-equal-but-distinct nested objects need a rule or a comparison of serialized values.

Negation

should().objects({ id: 1 }, { id: 2 }).not.equal(); // ok should().objects({ id: 1 }, { id: 1 }).not.equal(); // throws

not inverts equal() itself; the modifiers keep their configuring role. A null/undefined object throws The entry is not defined. even under not (see Concepts).

Signatures and messages

See API reference — Objects verifier.

02 сентября 2026