Skip to content

Jest Array and Object Matchers

Arrays and objects need their own set of matchers because you often care about containment, length, or a subset of properties rather than exact full equality. This lesson covers the most useful ones.

toContain() and toHaveLength()

.toContain() checks whether an array (or string, or any iterable) includes a specific primitive item. .toContainEqual() does the same but with deep equality, useful for arrays of objects. .toHaveLength() checks an array's (or string's) length property directly.

These matchers avoid manually calling .includes() or checking .length yourself, and they produce much clearer failure output when something doesn't match.

const fruits = ['apple', 'banana', 'cherry'];

expect(fruits).toContain('banana');
expect(fruits).toHaveLength(3);
expect([{ id: 1 }, { id: 2 }]).toContainEqual({ id: 2 });

.toContainEqual() is required (instead of .toContain()) whenever the array holds objects rather than primitives.

toHaveProperty() and toMatchObject()

expect(user).toHaveProperty('address.city', 'Austin');
expect(response).toMatchObject({ status: 200, body: { ok: true } });
  • .toHaveProperty(path, value?) checks a (possibly nested, dot-separated) property exists, optionally with a specific value.
  • .toMatchObject(subset) passes if the received object contains at least the given properties — extra properties are ignored.
  • Both matchers are ideal for testing large API responses where you only care about a few fields.
  • Use .toEqual() instead when you want to assert on the *entire* object, not a subset.

Array and Object Matchers Cheat Sheet

The matchers most often used for arrays and objects.

Matcher Checks
.toContain(item) Array/iterable includes a primitive item
.toContainEqual(item) Array includes an item, compared deeply
.toHaveLength(n) Array or string has exactly n elements/characters
.toHaveProperty(path, val?) Object has a (nested) property, optionally with a value
.toMatchObject(subset) Object contains at least these properties
.toEqual(arrayOrObject) Full deep equality
.arrayContaining([...]) Asymmetric matcher: array contains these items
.objectContaining({...}) Asymmetric matcher: object contains these properties

Asymmetric Matchers Inside toEqual()

expect.arrayContaining() and expect.objectContaining() can be nested inside .toEqual() to partially match part of a structure while still checking the rest exactly. This is useful when most of an object needs an exact check, but one field (like a generated timestamp) should be ignored or loosely matched.

expect(createOrder()).toEqual(
  expect.objectContaining({
    status: 'pending',
    items: expect.arrayContaining(['sku-123']),
  })
);

Any fields not listed (like an auto-generated id or createdAt) are ignored by the partial matchers.

Checking Deeply Nested Properties

.toHaveProperty() accepts a dot-separated path string (or an array of keys) to reach deeply nested values without manually chaining property access and risking a TypeError if an intermediate value is missing.

const user = {
  profile: {
    address: { city: 'Austin', zip: '78701' },
  },
};

expect(user).toHaveProperty('profile.address.city', 'Austin');
expect(user).toHaveProperty(['profile', 'address', 'zip'], '78701');

Common Mistakes

  • Using .toContain() on an array of objects instead of .toContainEqual().
  • Using .toEqual() when you only care about a subset of an object's fields, causing brittle tests.
  • Manually writing expect(arr.length).toBe(n) instead of the more readable .toHaveLength(n).
  • Forgetting .toHaveProperty() supports dot-notation and unnecessarily chaining property access by hand.

Key Takeaways

  • .toContain() is for primitives; .toContainEqual() is for objects inside arrays.
  • .toHaveLength() reads more clearly than manually checking .length.
  • .toHaveProperty() and .toMatchObject() are ideal for checking part of a larger object.
  • expect.arrayContaining()/expect.objectContaining() allow partial matches inside .toEqual().

Pro Tip

When testing an API response with a generated id or createdAt timestamp, reach for .toMatchObject() or expect.objectContaining() instead of stripping those fields out manually before comparing.