Why you should never use .toBe in Jest/Vitest
Tags: jest, bestpractices, unittesting, javascript
Published: 2020-07-21 00:21:56

![Patrick Stewart dressed as Hamlet with the text "toBe or toEqual, that is the question"](https://dev-to-uploads.s3.amazonaws.com/i/kjo13eg7e01au40zozf3.png)

Alright, this is going to be a short one. Let's just get to the point, what should you use instead, and why.


`.toEqual()` is a better choice in every scenario.

> *Wait... I thought these were the same thing, just aliased?*

Most do! And that right there is the problem. Here's the difference:

* `.toEqual` works based on *deep* equality
* `.toBe` is literally just doing a `Object.is(x, y)` under the hood. Which is [*slightly* different](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is), but basically the same as `x === y`.

Here is an example where the two differ:

```js
let x = { z: true };
let y = { z: true };

expect(x)
  .toBe(y); // FALSE

expect(x)
  .toEqual(y); // TRUE
```

Now sure, this confusion could have been avoided had these been named something like `.toDeepEqual()` and `.toStrictlyEqual()`. But that is not the world we live in! And it is unlikely to ever be, since they already have `.toStrictEqual()` built in which actually is closer to a deep equal, than a strict equal (`===`). Not at all confusing! 🤬

In most cases, you are comparing an **actual** value with a hard coded expectation.

```js
test('Generates kitten', () => {
  let kitten = generateKitten();

  expect(kitten)
    .toEqual({
      fluffy: true,
      type: 'kitty',
      tail: true,
      feet: 4
    });
});
```

So in these cases, `.toEqual()` gives you exactly what you want. It also shows a diff of the specific properties and values that do not match when a test fails.

![Screenshot of a test failing that uses toEqual. Jest highlights the part that didn't match in the diff](https://dev-to-uploads.s3.amazonaws.com/i/wv3c0l8scqu3gvu3i5lr.png)

> *But what about primatives? Like `undefined`, `4`, `'text'`, `true`?*

In these cases the `.toEqual` and the `.toBe` are equivalent, because the first thing they both check is if the values are strictly equal. So performance wise there is no difference. `.toEqual` just handles *more cases* if the strict equality fails on non-primatives.

> *Okay, so if it doesn't matter, then I can use either for primitives?*


You *can*... but you shouldn't. The naming of them is close enough that the subtle difference between when one should be used over the other isn't intuitive or obvious. You should default to using `.toEqual` in all cases to prevent any confusion. For the same reason that I [don't use `++x` or `x++`](https://www.youtube.com/watch?v=_EANG8ZZbRs&t=42m22s) in my codebases. I don't want to *assume* that the person that wrote that line of code understands the subtle differences between `.toEqual` and `.toBe` or the very subtle differences between `Object.is` and `===`. It's much safer to always use `.toEqual` so anyone in the codebase will follow this same approach. I'd rather just avoid the issue. Plus, consistency matters.

> *But what if I actually do want to test if something is a reference?*

Sure, here's that hypothetical, and where people might *wrongfully* tell you to use `.toBe`:

```js
// Two players, both happen to have the same name and age
const player1 = { name: 'John', age: 25 };
const player2 = { name: 'John', age: 25 };
const players = [player1, player2];

function getFirstPlayer () {
  return players[0];
}

test('getFirstPlayer', () => {
  // USING TOBE
  expect(getFirstPlayer())
    .toBe(player1); // passes

  expect(getFirstPlayer())
    .not.toBe(player2); // passes

  // USING TOEQUAL
  expect(getFirstPlayer())
    .toEqual(player1); // passes

  expect(getFirstPlayer())
    .not.toEqual(player2); // fails
});
```

In this example we actually want to know if a value is a reference. In most cases we don't want that, but here we do. So using `.toBe` works, but it isn't obvious to others that we are using it to validate that something is a reference. So, even though the test passes, it's not really a good choice. We should make the intent of our code **clear and obvious**.

Here is a more obvious approach. (Note the use of `.toEqual`)

```js
test('getFirstPlayer', () => {
  const result = getFirstPlayer();

  expect(result === player1)
    .toEqual(true);

  expect(result === player2)
    .toEqual(false);
});
```

**Benefits of this approach:**

1. Intent is obvious, and **Obvious is always better**. It is reasonable to expect other developers to be familiar with how `===` works and to understand we are purposefully checking if both variables reference the same value.
1. Consistency. Using `.toEqual` for this will match the rest of your tests. It should be the default choice when doing matchers.
1. Reduces reliance on assumptions. (Assuming everyone knows how `.toBe` works. **Which they don't.** I literally had to correct the stack overflow post after looking up the Jest source code).
1. Reduces need for communication. (*Verifying* that everyone knows how `.toBe`, and by extension, `Object.is` works.)

> *Ahh ok I get it. Reduce the cognitive overhead of choosing one over the other*

Yes, `.toBe` is never required, while `.toEqual` often is. So use it by default in all cases and completely avoid `.toBe`.

* * *

**Credits:**

* [Patrick Stewart contemplating B](https://www.youtube.com/watch?v=hA7lv1SDzno)