React Testing with toEqual() vs. toBe()
A common testing tool for assertions is the toBe() method. In conjunction with an expect method, this method can be used to check that the produced value is equal to the value passed to toBe().
For testing primitive data types such as stings and integers this works great, but it quickly falls apart when testing objects and functions. Why does an array with the same items as another array fail the toBe() assertion? To understand this, we must first dive into the topics of mutable and immutable objects as well as the strict equality operator.
What is the difference between mutable and immutable objects? First, what objects are considered mutable or immutable? Immutable objects include strings and numbers and are copied by value. What this means is that whenever a string or number is copied to make a new variable, that new variable has its own value that does not affect the object it was copied from.
To give an example of this, let’s look at the following code:
Here you can see we create a variable str1 and assign it a string value. We then create a new variable str2 that we assign the value of str1. After we reassign a new value to str1 and print both str1 and str2 we see that the two variables hold different values even though str2 was created from the value of str1.
Let’s next look at the same operations, but this time do them on arrays:
In this case when we change the values of nums1, the nums2 array’s values change as well. If the string for str2 was not affected by the change made to string str1, then why are the nums arrays affected? The nums arrays are affected because they are mutable objects. Mutable objects, which include arrays, objects and functions, assign copies of their values by reference instead of value. What this means is that all copies of a mutable object point to the same item in memory and thus each time you change one of them, you change the values for all copies that reference those values.
To remember the difference between mutable and immutable objects, we need to remember that mutable objects make copies by referencing the same instance of an object, while immutable objects make copies by creating a new individual instance of the object.
Now that we have immutable and mutable objects sorted out, we next need to look at equality operators. In programming, equality operators are a way to check if two variables are equivalent. In Javascript, the two base equality operators are “==” and “===”.
While these two operators perform the same basic check, there are subtle differences between a “==” and “===” equality operator. The first difference between the two operators, is the type check. The “==”equality operator does not strictly check the type of its two operands and instead will coerce them into having the same data type. The “===” equality operator uses a strict data type check and will return false if two items do not have the same type. Let’s look at what this looks like in action:
Here we can see that the first expression passes because the “==” operator coerces the string and integer data types to be equivalent and let the expression pass. The strict operator “===” however fails this expression because it will not coerce the data types and thus fails the expression based off the differing data types.
Now that we’ve looked at equality operators with immutable objects, let’s investigate mutable objects. Below I will show expressions performed between two identical, but separately created arrays, as well as an array that is a copy of another array:
Here we see expressions return true or false based off of their value’s reference. With the first equality operator expression we see a false return because the references of the compared values are different. Even though the contents of the arrays are identical, the instances in memory they are referencing are different so the expression fails the value check.
When we check to see if the original nums1 array and the copy nums3 are equivalent we find that they return a true value because they both have the same values and same reference in memory. It is very important to know what depth of equality you are testing for whenever you use these equality operators.
After covering immutable and mutable objects, as well as some of the equality operators, we are finally ready to talk about the difference between the toBe() and toEqual() methods.
The go- to method for many tests is toBe() as it works well with primitive data types. This is great if you want to check that a user’s age is a certain number or a customer has enough money in their bank account, but what if you want to check for a duplicate record? Using toBe() to check an object representing a user will fail if the records you are using are duplicates stored in separate memory instances. This is because toBe() uses a deep equality check on the items passed in and will reject two identical objects with separate references. (Also be careful when using toBe() because just like the “===” it will not coerce values with different data types).
In this instance you will want to use the toEqual() method. The toEqual() method does not perform a deep equality check on the two objects, it instead takes a recursive approach to compare the primitive values stored in the object and decides whether they are equivalent based on these results.
Testing is a very important part of the software development process. Testing effectively requires engineers to have a solid command of their tools. I hope that after reading this article, you have a better understanding of mutable and immutable objects, the differences between “==” and “===” and when to use toBe() and toEqual().
References
https://jestjs.io/docs/en/expect#toequalvalue
https://benmccormick.org/2016/06/04/what-are-mutable-and-immutable-data-structures-2