380

Let's say I have the following:

var myNumber = 5;
expect(myNumber).toBe(5);
expect(myNumber).toEqual(5);

Both of the above tests will pass. Is there a difference between toBe() and toEqual() when it comes to evaluating numbers? If so, when I should use one and not the other?

Lloyd Banks
  • 32,108
  • 50
  • 143
  • 228
  • in a nutshell: no difference between the two when comparing primitives; for objects -> `toEqual()` will compare by key/values-content; `toBe()` will compare by object reference. – Andre Elrico Jan 20 '20 at 08:41

7 Answers7

521

For primitive types (e.g. numbers, booleans, strings, etc.), there is no difference between toBe and toEqual; either one will work for 5, true, or "the cake is a lie".

To understand the difference between toBe and toEqual, let's imagine three objects.

var a = { bar: 'baz' },
    b = { foo: a },
    c = { foo: a };

Using a strict comparison (===), some things are "the same":

> b.foo.bar === c.foo.bar
true

> b.foo.bar === a.bar
true

> c.foo === b.foo
true

But some things, even though they are "equal", are not "the same", since they represent objects that live in different locations in memory.

> b === c
false

Jasmine's toBe matcher is nothing more than a wrapper for a strict equality comparison

expect(c.foo).toBe(b.foo)

is the same thing as

expect(c.foo === b.foo).toBe(true)

Don't just take my word for it; see the source code for toBe.

But b and c represent functionally equivalent objects; they both look like

{ foo: { bar: 'baz' } }

Wouldn't it be great if we could say that b and c are "equal" even if they don't represent the same object?

Enter toEqual, which checks "deep equality" (i.e. does a recursive search through the objects to determine whether the values for their keys are equivalent). Both of the following tests will pass:

expect(b).not.toBe(c);
expect(b).toEqual(c);

Hope that helps clarify some things.

Alexandr Nil
  • 156
  • 1
  • 5
  • 11
elreimundo
  • 5,516
  • 1
  • 11
  • 6
  • 17
    "For primitive types (e.g. numbers, booleans, strings, etc.), there is no difference between toBe and toEqual" - as it turns out this is not entirely true. `expect(0).toBe(-0)` will pass but `expect(0).toEqual(-0)` will fail. – mgol Feb 08 '17 at 09:51
  • 11
    tl;dr - `toBe` uses strict equality - compare by reference, `toEqual` uses property equivalence. Recommended to use `toEqual` for primitives – Drenai Dec 30 '17 at 13:29
  • 3
    So which one should we use for primitives, and why? Drenai, why do you recommend toEqual? – Patrick Szalapski Mar 07 '19 at 16:13
  • @PatrickSzalapski I can only guess at Denai's reasoning, but `toEqual` is much more careful about equality (`0 != -0`, `"hi" = new String("hi")`, etc.), so I'd recommend using `toEqual` **exclusively** unless you're actually concerned about reference equivalence. See all the checks `toEqual` makes in the `eq` method here: https://github.com/jasmine/jasmine/blob/master/src/core/matchers/matchersUtil.js – River Jan 07 '20 at 00:28
  • I think it's better to use toBe when comparing primitives to save the overhead that is done in toEqual. – GarfieldKlon Feb 20 '20 at 12:38
96

toBe() versus toEqual(): toEqual() checks equivalence. toBe(), on the other hand, makes sure that they're the exact same object.

I would say use toBe() when comparing values, and toEqual() when comparing objects.

When comparing primitive types, toEqual() and toBe() will yield the same result. When comparing objects, toBe() is a stricter comparison, and if it is not the exact same object in memory this will return false. So unless you want to make sure it's the exact same object in memory, use toEqual() for comparing objects.

Check this link out for more info : http://evanhahn.com/how-do-i-jasmine/

Now when looking at the difference between toBe() and toEqual() when it comes to numbers, there shouldn't be any difference so long as your comparison is correct. 5 will always be equivalent to 5.

A nice place to play around with this to see different outcomes is here

Update

An easy way to look at toBe() and toEqual() is to understand what exactly they do in JavaScript. According to Jasmine API, found here:

toEqual() works for simple literals and variables, and should work for objects

toBe() compares with ===

Essentially what that is saying is toEqual() and toBe() are similar Javascripts === operator except toBe() is also checking to make sure it is the exact same object, in that for the example below objectOne === objectTwo //returns false as well. However, toEqual() will return true in that situation.

Now, you can at least understand why when given:

var objectOne = {
    propertyOne: str,
    propertyTwo: num    
}

var objectTwo = {
    propertyOne: str,
    propertyTwo: num    
}

expect(objectOne).toBe(objectTwo); //returns false

That is because, as stated in this answer to a different, but similar question, the === operator actually means that both operands reference the same object, or in case of value types, have the same value.

Community
  • 1
  • 1
Adjit
  • 9,403
  • 8
  • 45
  • 86
  • 5
    This avoids answering the question. You explain what `toEqual()` does by saying that *`toEqual()` checks equivalence*, but the obvious next question is *okay, so what does "equivalent" mean?* A description of the algorithm used to determine "equivalence", or at least examples of cases where the behaviour of `toEqual()` and `toBe()` differ, would render this more useful. – Mark Amery Jan 23 '15 at 11:07
  • 10
    Not only does this not answer the question, but it is ***wrong***. `toEqual` should be used for deep comparison between objects, not `toBe`. http://jsfiddle.net/bBL9P/67/ – Lloyd Banks Feb 04 '15 at 21:08
  • 3
    It seems people aren't bothering to test if what they are saying is correct. Both toBe and toEqual seem to be strict comparisons. Test it... So in my testing im yet to find a difference. for instance: var f = 1; var g = "1" expect(f == g).toEqual(true);//true expect(f).toEqual(g);//false expect(f).toBe(g);//false – user1809104 May 18 '15 at 09:00
  • 7
    This is completely wrong. `toEqual` is **not at all** the same as `==`. – meager Sep 08 '15 at 21:39
  • @meagar Why is that? I tried looking at the source code for `toEqual`, but wasn't able to figure out the details. I know `toBe` is `===`. Where can I find `customEqualityTester`? – Adjit Sep 08 '15 at 21:55
  • 6
    Read the comments above. `expect(1).toEqual('1')` fails, while `1 == '1'` is true. `toEqual` has nothing to do with `==`. It's like `===` except that it will compare objects in a manner similar to by-value comparison. – meager Sep 08 '15 at 21:58
34

To quote the jasmine github project,

expect(x).toEqual(y); compares objects or primitives x and y and passes if they are equivalent

expect(x).toBe(y); compares objects or primitives x and y and passes if they are the same object

Tharaka
  • 2,205
  • 1
  • 18
  • 21
14

Looking at the Jasmine source code sheds more light on the issue.

toBe is very simple and just uses the identity/strict equality operator, ===:

  function(actual, expected) {
    return {
      pass: actual === expected
    };
  }

toEqual, on the other hand, is nearly 150 lines long and has special handling for built in objects like String, Number, Boolean, Date, Error, Element and RegExp. For other objects it recursively compares properties.

This is very different from the behavior of the equality operator, ==. For example:

var simpleObject = {foo: 'bar'};
expect(simpleObject).toEqual({foo: 'bar'}); //true
simpleObject == {foo: 'bar'}; //false

var castableObject = {toString: function(){return 'bar'}};
expect(castableObject).toEqual('bar'); //false
castableObject == 'bar'; //true
Tamlyn
  • 18,041
  • 10
  • 96
  • 117
2

toEqual() compares values if Primitive or contents if Objects. toBe() compares references.

Following code / suite should be self explanatory :

describe('Understanding toBe vs toEqual', () => {
  let obj1, obj2, obj3;

  beforeEach(() => {
    obj1 = {
      a: 1,
      b: 'some string',
      c: true
    };

    obj2 = {
      a: 1,
      b: 'some string',
      c: true
    };

    obj3 = obj1;
  });

  afterEach(() => {
    obj1 = null;
    obj2 = null;
    obj3 = null;
  });

  it('Obj1 === Obj2', () => {
    expect(obj1).toEqual(obj2);
  });

  it('Obj1 === Obj3', () => {
    expect(obj1).toEqual(obj3);
  });

  it('Obj1 !=> Obj2', () => {
    expect(obj1).not.toBe(obj2);
  });

  it('Obj1 ==> Obj3', () => {
    expect(obj1).toBe(obj3);
  });
});
BeingSuman
  • 2,125
  • 6
  • 23
  • 41
1

Thought someone might like explanation by (annotated) example:

Below, if my deepClone() function does its job right, the test (as described in the 'it()' call) will succeed:

describe('deepClone() array copy', ()=>{
    let source:any = {}
    let clone:any = source
    beforeAll(()=>{
        source.a = [1,'string literal',{x:10, obj:{y:4}}]
        clone = Utils.deepClone(source) // THE CLONING ACT TO BE TESTED - lets see it it does it right.
    })
    it('should create a clone which has unique identity, but equal values as the source object',()=>{
        expect(source !== clone).toBe(true) // If we have different object instances...
        expect(source).not.toBe(clone) // <= synonymous to the above. Will fail if: you remove the '.not', and if: the two being compared are indeed different objects.
        expect(source).toEqual(clone) // ...that hold same values, all tests will succeed.
    })
})

Of course this is not a complete test suite for my deepClone(), as I haven't tested here if the object literal in the array (and the one nested therein) also have distinct identity but same values.

Jared Tomaszewski
  • 743
  • 4
  • 15
  • 30
1

I think toEqual is checking deep equal, toBe is the same reference of 2 variable

  it('test me', () => {
    expect([] === []).toEqual(false) // true
    expect([] == []).toEqual(false) // true

    expect([]).toEqual([]); // true // deep check
    expect([]).toBe([]); // false
  })