Testing Arrays

Just like strings and numbers, there are also helpers built into Jest and Vitest to make assertions against arrays.

That includes things like:

  • Checking the size of an array
  • Checking that a single item is in an array (or not in the array)
  • Checking an array has the same internal structure as another array
  • Checking partial matches (some items match)

Read on in this lesson to find out how to do all of that and more.

Checking an array is the exact same object (toBe())

Let's start with the simplest of checks. You can check two variables are both the same reference to the same array object.

const returnSameArray = inputArray =>
  inputArray;

test('it returns the same array object', () => {
  const originalArray = ['a', 'b'];

  expect(
    returnSameArray(originalArray)
  ).toBe(originalArray);

  // these would fail as they're
  // different object references:
  // expect(originalArray).toBe(['a', 'b']) // ❌ fail
  // expect(originalArray).toBe([...originalArray]) // ❌ fail
});

Compare the array contents are the same (toEqual() and toStrictEqual())

It is very common to check that an array's contents exactly match, but you don't care that the array object reference itself has changed.

For this you can use toEqual (performs deep equality checking) and toStrictEqual (performs strict deep equality checking).

If possible, I always try to use toStrictEqual() if possible. See further down with a few more details about the difference when it comes to arrays.

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

test('array equality', () => {
  expect(fruits).toEqual([
    'apple',
    'banana',
    'orange',
  ]);
  expect(fruits).toStrictEqual([
    'apple',
    'banana',
    'orange',
  ]);
});

toEqual vs toStrictEqual

toEqual does a deep equality check, but you should be aware of these things:

  • Ignores undefined properties in objects
  • Doesn't differentiate between sparse and dense arrays
  • Does not check object prototypes

But toStrictEqual is much stricter:

  • Checks that undefined properties are explicitly defined
  • Differentiates between sparse and dense arrays
  • Strict checks when it comes to checking if an object prototype is the exact same type
// These will behave the same for simple arrays
expect([1, 2, 3]).toEqual([1, 2, 3]); // ✅ pass
expect([1, 2, 3]).toStrictEqual([
  1, 2, 3,
]); // ✅ pass

// Sparse arrays - this is where they differ
const sparse = [1, , 3]; // has undefined at index 1
const dense = [1, undefined, 3]; // explicitly has undefined

expect(sparse).toEqual(dense); // ✅ pass
expect(sparse).toStrictEqual(dense); // ❌ fail

// Arrays with objects containing undefined properties
const arr1 = [{ a: 1, b: undefined }];
const arr2 = [{ a: 1 }];

expect(arr1).toEqual(arr2); // ✅ pass
expect(arr1).toStrictEqual(arr2); // ❌ fail

Object type checking with toStrictEqual

Another important difference is how they handle object types and prototypes. When you use toStrictEqual, it requires the same prototype parent.

class User {
  constructor(name) {
    this.name = name;
  }
}
const classArray = [new User('Alice')];
const plainArray = [{ name: 'Alice' }];
expect(classArray).toEqual(plainArray); // ✅ pass - only checks properties
expect(classArray).toStrictEqual(
  plainArray
); // ❌ fail - different prototypes

// Arrays with different prototypes
const arr = [1, 2, 3];
const arrLike = Object.assign(
  Object.create(null),
  [1, 2, 3]
);
arrLike.length = 3;
expect(arr).toEqual(arrLike); // ✅ pass
expect(arr).toStrictEqual(arrLike); // ❌ fail

Deep equality checks in arrays

toEqual() and toStrictEqual() work with deep equality checks (check inner contents).

const array1 = ['a', [1]];
const array2 = ['a', [1]];

test('deep equality toEqual', () => {
  expect(array1).toEqual(array2);
});

test('deep equality toStrictEqual', () => {
  expect(array1).toStrictEqual(array2);
});

Checking array lengths

This is very simple, as we can use the .toHaveLength() function - the same as you saw in the string lesson.

test('array length', () => {
  const numbers = [1, 2, 3, 4, 5];
  expect(numbers).toHaveLength(5);
  expect(numbers.length).toBe(5);
});

Checking an array contains an item

You will often write tests where you just care about one item in a list.

test('array contains items', () => {
  const colors = [
    'red',
    'green',
    'blue',
  ];
  expect(colors).toContain('red');
  expect(colors).toContain('green');
});

You can of course use .not to check it does not contain an item

expect(colors).not.toContain('purple');

But if you need to check it contains an item that is an object (not a primitive like a string in the example above) then you must use .toContainEqual().

test('array contains objects', () => {
  const colors = [
    { color: 'red' },
    { color: 'green' },
  ];

  // Use toContainEqual for deep equality comparison
  expect(colors).toContainEqual({
    color: 'red',
  }); // ✅ pass

  // toContain only works with reference equality for objects
  // expect(colors).toContain({ color: 'red' }); // ❌ fail

  // toContain works when checking the exact same object reference
  expect(colors).toContain(colors[1]); // ✅ pass
});

Partial matches

I always find this a bit confusing and hard to read, but really it is quite simple.

expect.arrayContaining() is also useful when you only want to test if a certain item(s) are in an array, ignoring anything else in the array:

test('array contains subset', () => {
  const listOfLetters = [
    'a',
    'b',
    'c',
    'd',
    'e',
  ];

  // Check that array contains at least these items (order doesn't matter)
  expect(listOfLetters).toEqual(
    expect.arrayContaining(['b', 'd'])
  ); // ✅ pass

  // This would fail because 'z' is not in the array
  expect(listOfLetters).not.toEqual(
    expect.arrayContaining(['b', 'z'])
  ); // ✅ pass (negative assertion)
});

Testing array subsets of partial object matches

(If this is starting to get confusing, stay with it! It will make sense soon)

You can use a mix of toContain or toContainEqual(), but with expect.arrayContaining(). You can also mix this with expect.objectContaining()

test('array contains object', () => {
  const users = [
    { id: 1, name: 'Bobby' },
    { id: 2, name: 'Jane' },
    { id: 3, name: 'Bob' },
  ];

  expect(users).toContainEqual({
    id: 2,
    name: 'Jane',
  });

  expect(users).toEqual(
    expect.arrayContaining([
      expect.objectContaining({
        name: 'Bob',
      }),
    ])
  );

  // Negative example: should not find a user named 'Alice'
  expect(users).not.toEqual(
    expect.arrayContaining([
      expect.objectContaining({
        name: 'Alice',
      }),
    ])
  );
});

There will be a further lesson on this later on, where there will be much more detail.

Testing array order

There are a few approaches to testing array order.

The easiest is probably to reconstruct an array in the correct order, and then compare with .toEqual or .toStrictEqual:

test('array order matters', () => {
  const ordered = [1, 2, 3];
  const unordered = [3, 1, 2];

  expect(ordered).toEqual([1, 2, 3]);
  expect(unordered).not.toEqual([
    1, 2, 3,
  ]);
});

Sometimes this is hard to do, so I find sometimes transforming the data in the test is the most readable way.

const complexArray = [
  { name: 'bob', age: 90 },
  { name: 'jane', age: 20 },
  { name: 'brian', age: 50 },
];

const sortByAge = people => {
  return people.sort(
    (a, b) => a.age - b.age
  );
};

// we know ordered by age, it should be jane, brian, bob

test('sorting returns array in expected order', () => {
  const sortedArray = sortByAge(
    complexArray
  );

  expect(
    sortedArray.map(
      person => person.name
    )
  ).toEqual(['jane', 'brian', 'bob']);
});

Checking if an array is empty

There are a few ways to do it. toHaveLength(0) is probably the clearest.

test('empty arrays', () => {
  const empty = [];
  expect(empty).toHaveLength(0);
  expect(empty).toEqual([]);
});

Checking a variable is an array instance

Sometimes you need to test if something is actually an Array instance:

test('check if value is an array', () => {
  const arr = [1, 2, 3];
  const notArr = {
    0: 1,
    1: 2,
    2: 3,
    length: 3,
  }; // array-like object

  expect(Array.isArray(arr)).toBe(true);
  expect(Array.isArray(notArr)).toBe(
    false
  );

  // You can also use toBeInstanceOf
  expect(arr).toBeInstanceOf(Array);
  expect(notArr).not.toBeInstanceOf(
    Array
  );
});

Cheat Sheet

toBe() - Same array reference

  • Checks References: ✅ Yes
  • Deep Equality: ❌ No

toEqual() - Same contents

  • Checks References: ❌ No
  • Deep Equality: ✅ Yes
  • Strict Type Checking: ❌ No

toStrictEqual() - Same contents, strict

  • Checks References: ❌ No
  • Deep Equality: ✅ Yes
  • Strict Type Checking: ✅ Yes

toContain() - Has specific item

  • Checks References: ✅ Yes
  • Deep Equality: ❌ No (primitives only)

toContainEqual() - Has specific object/array

  • Checks References: ❌ No
  • Deep Equality: ✅ Yes
  • Strict Type Checking: ❌ No

Lesson Task

In the code editor (

use the tabs above

), use a mix of .toEqual() and .toStrictEqual() to get the tests passing.

Ready to try running tests for this lesson?