What is the difference between 'toBe' and 'toEqual' in Jest?

TestingJestjs

Testing Problem Overview


Jest documentation reads:

> toBe just checks that a value is what you expect. It uses === to check strict equality.

And for toEqual: > Use .toEqual when you want to check that two objects have the same value. This matcher recursively checks the equality of all fields, rather than checking for object identity—this is also known as "deep equal". For example, toEqual and toBe behave differently in this test suite, so all the tests pass.

const x = { a: { b: 3 } };
const y = { a: { b: 3 } };

expect(x).toEqual(y);
expect(x).toBe(y);

In this case, toEqual passes but toBe fails. I understand that toEqual passes because it does a deep equal check. Why is toBe failing in this case?

Also, are there best practices for using toBe and toEqual (not just in Jest but in other testing frameworks, too)?

Testing Solutions


Solution 1 - Testing

It fails cause x and y are different instances and not equal as in (x === y) === false. You can use toBe for primitives like strings, numbers or booleans for everything else use toEqual. For example

x = 4 
y = 4
x === y // true

x = 'someString'
y = 'someString'
x === y // true

Even empty objects are not equal

x = {}
y = {}
x === y //false

Solution 2 - Testing

Suppose there are two players with same name and both of them scored 20.

let player1 = {
    name: "Amit",
    score: 20,
}

let player2 = {
    name: "Amit",
    score: 20,
}

Now I have a function which gives me 1st player.

function getFirstPlayer(player1,player2){
    return player1;
}

How will I test this function?

# 1st way
expect(getFirstPlayer(player1,player2)).toBe(player1); // Passes
expect(getFirstPlayer(player1,player2)).not.toBe(player2); // Passes

# 2nd way
expect(getFirstPlayer(player1,player2)).toEqual(player1); // Pases
expect(getFirstPlayer(player1,player2)).not.toEqual(player2); // Fails

toBe tests Identity and toEqual tests features. So twin kids can have same features but their real identity is different from each other. The way this function is designed we should use toBe.

Now I have a another function which increases the player score.

function addScore(player,scoreToAdd){
    player.score += scoreToAdd;
}

How will I test this function?

# 1st way
addScore(player1,20);
expect(player1).toBe({name:"Amit", score:40});  // Fails

# 2nd way
addScore(player1,20);
expect(player1).toEqual({name:"Amit", score:40});  // Passes

Did you notice that in 1st way we are passing a new player like entity in right hand side. Is there any chance that player1 has the same identity of newly created entity? Nope. So toBe will always fail in this case.

2nd way passes as in toEqual we are comparing features. Here player1 and newly created entity has same features.

Note: In context of javascript, primitive values like "Amit" is identity in itself. So

expect("Amit").toBe("Amit") // Passes

I have written this answer for particular case, when you get this idea of identity and features, you can implement it in your scenario.

Solution 3 - Testing

You have also toStrictEqual() since Jest v23

Explanations: https://jestjs.io/docs/en/expect#tostrictequalvalue

There is an ESLint plugin for Jest with a rule to enforce toStrictEqual(): https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/prefer-strict-equal.md

npm install --save-dev eslint-plugin-jest

// .eslintrc.js
module.exports = {
  extends: [
    'plugin:jest/recommended'
  ],

  rules: {
    'jest/prefer-strict-equal': 'error'
  }
}

Solution 4 - Testing

It is all about object references.

.toBe compares primitive values or checks referential identity of object instances. On the other hand, toEqual looks for deep equailty.

expect({ name: 'john doe' }).toEqual({ name: 'john doe'}); // PASSES
expect({ name: 'john doe' }).toBe({ name: 'john doe'});    // FAILS

The second assertion fails because they are different instances even though they are deeply equal. Remember, object literal syntax creates a new instance of the base object.

let a = { name: 'john doe' }; let b = a;

Here, the assignment operator copies the object reference stored in the variable a to b.

expect(a).toBe(b); // PASSES

The assertion passes because 'a' and 'b' points to same object. Please note that { name: 'john doe' } is not an object, it is an instruction to create one. Objects live in memory and interacted with via their references stored in a variable.

Solution 5 - Testing

There's a few people saying that .toBe() is the same as x === y, but it's actually slightly different. Jest uses Object.is(x, y) when doing expect(x).toBe(y).

Unless you are verifying if a value is the same as a reference (like when checking if something got deepcloned properly), you should always use .toEqual(). And even in the deepclone example, I think it is cleaner to just do expect(x === y).toEqual(true) just to remove any confusion as to what you are trying to do.

You should not expect others to know the differences between toBe and toEqual, or to even know that Object.is exists and how it differs from ===. To avoid communication problems and testing issues, always use .toEqual, never use .toBe.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsshhView Question on Stackoverflow
Solution 1 - TestingAndreas KöberleView Answer on Stackoverflow
Solution 2 - Testingamit77309View Answer on Stackoverflow
Solution 3 - Testingtanguy_kView Answer on Stackoverflow
Solution 4 - TestingsnnsnnView Answer on Stackoverflow
Solution 5 - TestingJaredcheedaView Answer on Stackoverflow