Write JavaScript like a pro. Javascript Icon

Follow the ultimate JavaScript roadmap.

Jest: toEqual or toStrictEqual? The difference.

When starting out with Jest Matchers for unit testing, it’s common to hear “should I use toEqual or toStrictEqual?”.

Well, it depends on your object equality.

This article is dedicated to demonstrating the differences between toEqual and toStrictEqual, so you can make a decision which to use and when.

First, let’s start off with the follow basic data structure (an object literal):

const pizza = {
  name: 'Flaming Sizzler',
  price: 899,
};

And drop it in a simple test:

test('...', () => {
  const pizza = {
    name: 'Flaming Sizzler',
    price: 899,
  };

  // ✅ PASS
  expect(pizza).toEqual({
    name: 'Flaming Sizzler',
    price: 899,
  });
});

Okay that makes sense, so what if we use a constructor pattern to create a class to test with?

test('...', () => {
  class Pizza {
    constructor(name, price) {
      this.name = name;
      this.price = price;
    }
  }

  // ✅ PASS
  expect(new Pizza('Flaming Sizzler', 899)).toEqual({
    name: 'Flaming Sizzler',
    price: 899,
  });
});

Great! Uh, wait, is it great? No! Kinda… Maybe.

Despite the two objects being comparable on the top level public properties, their underlying implementation is NOT the same. Their prototypes differ.

There are two different data types. class Pizza is a custom object, whereas our toEqual is expecting an object literal.

These may “look” the same in your head, or developer tools console.log, but they are not the same.

Meaning, this boils down to object equality…

So when we change to using toStrictEqual, the error then presents itself:

test('...', () => {
  class Pizza {
    constructor(name, price) {
      this.name = name;
      this.price = price;
    }
  }

  // ❌ FAIL
  expect(new Pizza('Flaming Sizzler', 899)).toStrictEqual({
    name: 'Flaming Sizzler',
    price: 899,
  });
});

Jest uses a deep equality check to determine the type differences.

You’ll need to use toStrictEqual to verify that your data types are in fact the same.

You could also use the object literal approach combined with Jest’s .not inversion:

test('...', () => {
  class Pizza {
    constructor(name, price) {
      this.name = name;
      this.price = price;
    }
  }

  // ❌ FAIL
  expect({ name: 'Flaming Sizzler', price: 899 }).not.toStrictEqual({
    name: 'Flaming Sizzler',
    price: 899,
  });

  // ✅ PASS
  expect(new Pizza('Flaming Sizzler', 899)).not.toStrictEqual({
    name: 'Flaming Sizzler',
    price: 899,
  });
});

The object literals that fail are the same, therefore error. The Pizza type’s shape is strictly equal to the object literal, but it’s type is made optional in the test context.

One more thing with toEqual is that it doesn’t take undefined values (empty keys) into account, let’s add promo: undefined and see the result:

test('...', () => {
  const pizza = {
    promo: undefined,
    name: 'Flaming Sizzler',
    price: 899,
  };

  // ✅ PASS
  expect(pizza).toEqual({
    name: 'Flaming Sizzler',
    price: 899,
  });

  // ❌ FAIL
  expect(pizza).toStrictEqual({
    name: 'Flaming Sizzler',
    price: 899,
  });
});

Obviously we are hard-coding the const pizza but in a real-world environment your data structure is likely going to be generated on-the-fly for your testing.

🚀 There’s so much more to JavaScript! Fast track your skills overnight with my masterclass JavaScript Courses and take your skills to the top.

Using toStrictEqual should be your default go-to method instead of toEqual for the above reasons.

Happy testing!

Learn JavaScript the right way.

The most complete guide to learning JavaScript ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover