Testing objects equality in NodeJS
We are writing tests for a program. We want to write a functionnal test that verifies that the output of the program matches some expectation.
The object returned is a complex JS object (with nested objects, many properties… etc).
We want to test that this obviously matches what we need.
Up until now, we were “browsing” the object and the expected outcome, checking for each property, and each nested object. That is very cumbersome and we were wondering if there was any library that would ‘build’ all the tests, based just on the object. Something like this for example.
var res = {
a: {
alpha: [1,2,3],
beta: "Hello",
gamma: "World"
},
},
b: 123,
c: "It depends"
}
};
var expectation = {
a: {
alpha: [1,2,4],
beta: "Hello",
gamma: "World"
},
},
b: 123,
c: "It depends"
}
};
assert(res, expectation) // -> Raises an error because res[a][b][2] is different from expectation[a][b][2].
[In the example, I have simplified the complexity of our object…]
I should insist on the fact that we need a piece of code that is smart enough to tell us what is different, rather than just tell us that the 2 objects are different. We now about deep equality, but we haven’t found anything that actually tells us the differences.
Node has the built in assert module meant for testing. This has a method called deepEqual for deep equality checking.
Function signature is:
assert.deepEqual(actual, expected, [message])
Quickly written function for testing deepEquality and returning diff:
// Will test own properties only
function deepEqualWithDiff(a, e, names){
var dif = {};
var aKeys = Object.keys(a);
var eKeys = Object.keys(e);
var cKeys = aKeys;
var dKeys = eKeys;
var c = a;
var d = e;
var names = {
c: names ? names['a'] : 'Actual',
d: names ? names['e'] : 'Expected'
}
if(eKeys.length > aKeys.length){
cKeys = eKeys;
dKeys = aKeys;
c = e;
d = a;
names = {
d: names ? names['a'] : 'Actual',
c: names ? names['e'] : 'Expected'
}
}
for(var i = 0, co = cKeys.length; i < co; i++){
var key = cKeys[i];
if(typeof c[key] !== typeof d[key]){
dif[key] = 'Type mismatch ' + names['c'] + ':' + typeof c[key] + '!==' + names['d'] + typeof d[key];
continue;
}
if(typeof c[key] === 'function'){
if(c[key].toString() !== d[key].toString()){
dif[key] = 'Differing functions';
}
continue;
}
if(typeof c[key] === 'object'){
if(c[key].length !== undefined){ // array
var temp = c[key].slice(0);
temp = temp.filter(function(el){
return (d[key].indexOf(el) === -1);
});
var message="";
if(temp.length > 0){
message += names['c'] + ' excess ' + JSON.stringify(temp);
}
temp = d[key].slice(0);
temp = temp.filter(function(el){
return (c[key].indexOf(el) === -1);
});
if(temp.length > 0){
message += ' and ' + names['d'] + ' excess ' + JSON.stringify(temp);
}
if(message !== ''){
dif[key] = message;
}
continue;
}
var diff = deepEqualWithDiff(c[key], d[key], {a:names['c'],e:names['d']});
if(diff !== true && Object.keys(diff).length > 0){
dif[key] = diff;
}
continue;
}
// Simple types left so
if(c[key] !== d[key]){
dif[key] = names['c'] + ':' + c[key] + ' !== ' + names['d'] + ':' + d[key];
}
}
return Object.keys(dif).length > 0 ? dif : true;
}
JavaScript doesn’t support object deep equality out of the box and I am not aware of anything built into the NodeJS API either.
My bet would probably be Underscore and the isEqual function.
npm install underscore
var _ = require('underscore');
var moe = {name : 'moe', luckyNumbers : [13, 27, 34]};
var clone = {name : 'moe', luckyNumbers : [13, 27, 34]};
moe == clone;
=> false
_.isEqual(moe, clone);
=> true
Although most Node testing frameworks should also contain an object deep equality assertion but you didn’t mention which one you are using.
deep-diff does what OP is asking for. It can compare two javascript objects / JSON objects and list the differences in a way that your code can access.
This is an old question so this library probably didn’t exist at the time the question was asked.
Note that Node’s deepEqual()
is deprecated since Node.js v9.9.0, use deepStrictEqual() instead:
const assert = require('assert');
const actual = {
a: {
name: 'some',
b: {
name: 'thing',
},
},
};
const expected = {
a: {
name: 'thing',
b: {
name: 'some',
},
},
};
assert.deepStrictEqual(actual, expected, 'Expect names to be equal.');
Pro Tip: If you don’t use a custom message in deepStrictEqual
, you will get a nice colorful output of the differences between the two objects:
While I think the basics of object comparison have already been covered (see here and others), if you’re looking for a quick and dirty way to execute these tests and see the diff between two non-equal objects, you can just run your tests using nodeunit within the Webstorm IDE (here)
Webstorm integrates particularly well with nodeunit, and for any test.equals() or test.deepEquals() assertions it provides a viewable diff with highlighting to show your descrepencies. I highly recommend the IDE for how well it integrates testing into my js development cycle.
Now, if you need the results of that test/diff to be accessible within your code, this obviously isn’t enough for you and I’d recommend copying a couple of the deepEquals comparators from the first link I listed.
Good luck,
Brian
This is a simple/flexible deep diff function I wrote using Lodash:
_.merge(obj1, obj2, function (objectValue, sourceValue, key, object, source) {
if ( !(_.isEqual(objectValue, sourceValue)) && (Object(objectValue) !== objectValue)) {
console.log(key + "\n Expected: " + sourceValue + "\n Actual: " + objectValue);
}
});
You can replace the console.log()
statement with whatever comparison logic you need. Mine just prints out the differences to the console.