Jasmine JavaScript Testing - toBe vs toEqual
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?
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.
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.
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
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
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);
});
});