Jasmine toEqual for complex objects (mixed with functions)

If anyone is using node.js like myself, the following method is what I use in my Jasmine tests when I am only concerned with comparing the simple properties while ignoring all functions. This method requires json-stable-stringify which is used to sort the object properties prior to serializing.

Usage:

  var stringify = require('json-stable-stringify');

  var obj1 = {
    func: function() {
    },
    str1: 'str1 value',
    str2: 'str2 value',
    nest1: {
    nest2: {
        val1:'value 1',
        val2:'value 2',
        someOtherFunc: function() {
        }
      }
    }
  };

  var obj2 = {
    str2: 'str2 value',
    str1: 'str1 value',
    func: function() {
    },
    nest1: {
      nest2: {
        otherFunc: function() {
        },
        val2:'value 2',
        val1:'value 1'
      }
    }
  };

  it('should compare object properties', function () {
    expect(stringify(obj1)).toEqual(stringify(obj2));
  });

As Vlad Magdalin pointed out in the comments, making the object to a JSON string, it can be as deep as it is, and functions and File/FileList class. Of course, instead of toString() on the function, it could just be called 'Function'

function replacer(k, v) {
    if (typeof v === 'function') {
        v = v.toString();
    } else if (window['File'] && v instanceof File) {
        v = '[File]';
    } else if (window['FileList'] && v instanceof FileList) {
        v = '[FileList]';
    }
    return v;
}

beforeEach(function(){
    this.addMatchers({
        toBeJsonEqual: function(expected){
            var one = JSON.stringify(this.actual, replacer).replace(/(\\t|\\n)/g,''),
                two = JSON.stringify(expected, replacer).replace(/(\\t|\\n)/g,'');

                return one === two;
            }
    });
});

expect(obj).toBeJsonEqual(obj2);

Try the Underscore _.isEqual() function:

expect(_.isEqual(obj1, obj2)).toEqual(true);

If that works, you could create a custom matcher:

this.addMatchers({
    toDeepEqual: function(expected) {
        return _.isEqual(this.actual, expected);
    };
});

You can then write specs like the following:

expect(some_obj).toDeepEqual(expected_obj);

Extending @Vlad Magdalin's answer, this worked in Jasmine 2:

http://jasmine.github.io/2.0/custom_matcher.html

beforeEach(function() {
  jasmine.addMatchers({
    toDeepEqual: function(util, customEqualityTesters) {
      return {
        compare: function(actual, expected) {
          var result = {};
          result.pass = _.isEqual(actual, expected);
          return result;
        }
      }
    }
  });
});

If you're using Karma, put that in the startup callback:

callback: function() {
  // Add custom Jasmine matchers.
  beforeEach(function() {
    jasmine.addMatchers({
      toDeepEqual: function(util, customEqualityTesters) {
        return {
          compare: function(actual, expected) {
            var result = {};
            result.pass = _.isEqual(actual, expected);
            return result;
          }
        }
      }
    });
  });

  window.__karma__.start();
});