Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Karma/Jasmine: Pretty printing object comparison

I'm currently using the Karma test runner for my Angular project, with the jasmine testing framework. It's working great, but I have one issue: When an object comparison fails, the resulting print into the console is really hard to read, and gets harder the more properties these objects have. Example:

Expected spy spy to have been called with [ { currentCareMoment : { ID : 5, Description : 'Late namiddag (16-20)', StartHour : 16, EndHour : 20 }, previousCareMoment : { ID : 4, Description : 'Namiddag (14-16)', StartHour : 14, EndHour : 16 } } ] but actual calls were [ { currentCareMoment : { ID : 6, Description : 'Avond (20-24)', StartHour : 20, EndHour : 24 }, previousCareMoment : { ID : 5, Description : 'Late namiddag (16-20)', StartHour : 16, EndHour : 20 } } ].

Is there anyway to set up Jasmine (as I think Karma has nothing to do with it) to print objects prettier? Just some line breaks and indentation would already be a huge help. Example:

Expected spy spy to have been called with [ { 
  currentCareMoment : { 
    ID : 5, 
    Description : 'Late namiddag (16-20)', 
    StartHour : 16, 
    EndHour : 20 
  }, 
  previousCareMoment : { 
    ID : 4, 
    Description : 'Namiddag (14-16)', 
    StartHour : 14, 
    EndHour : 16 
  } 
} ] but actual calls were [ { 
  currentCareMoment : { 
    ID : 6, 
    Description : 'Avond (20-24)', 
    StartHour : 20, 
    EndHour : 24 
  }, 
  previousCareMoment : { 
    ID : 5, 
    Description : 'Late namiddag (16-20)', 
    StartHour : 16, 
    EndHour : 20 
  } 
} ].
like image 893
SimonVanCasteren Avatar asked May 15 '14 11:05

SimonVanCasteren


3 Answers

Since the time that the other answers here were added, a pretty-printing option became available in karma-jasmine-diff-reporter. I would suggest trying it -- it's very configurable and is working nicely for me in combination with other common test reporters.

A minimal configuration looks like:

    reporters: ['jasmine-diff'],

    jasmineDiffReporter: {
        multiline: true,
        pretty: true
    },
like image 190
Ben Regenspan Avatar answered Nov 18 '22 02:11

Ben Regenspan


My answer is based on jasmine 2.0.1.

Method 1 is documented in the jasmine docs. So it is probably recommended.
Method 2 however is much simpler.

Method 1: Using a custom matcher

My initial though was to create a custom matcher as described here. So I copied the toHaveBeenCalledWith matcher from the jasmine source code and modified it so it would pretty print:

var matchers = {
  toBeCalledWith: function (util, customEqualityTesters) {
    return {
      compare: function() {
        var args = Array.prototype.slice.call(arguments, 0),
          actual = args[0],
          expectedArgs = args.slice(1),
          result = { pass: false };

        if (!jasmine.isSpy(actual)) {
          throw new Error('Expected a spy, but got ' + jasmine.JSON.stringify(actual, undefined, 2) + '.');
        }

        if (!actual.calls.any()) {
          result.message = function() {
            return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + JSON.stringify(expectedArgs, undefined, 2) + ' but it was never called.';
          };
          return result;
        }

        if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) {
          result.pass = true;
          result.message = function() {
            return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + JSON.stringify(expectedArgs, undefined, 2) + ' but it was.';
          };
        } else {
          result.message = function() {
            return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + JSON.stringify(expectedArgs, undefined, 2) + ' but actual calls were ' + JSON.stringify(actual.calls.allArgs(), undefined, 2) + '.';
          };
        }

        return result;
      }
    };
  }
};

The test case would then use our new matcher instead:

describe('Test', function() {

  beforeEach(function() {
    jasmine.addMatchers(matchers);
  });

  it('should print pretty', function() {
    var spy = jasmine.createSpy('spy');
    spy({
      currentCareMoment: {
      ID: 5,
      Description: 'Late namiddag (16-20)',
      StartHour: 16,
      EndHour: 20
    },
    previousCareMoment: {
      ID: 4,
      Description: 'Namiddag (14-16)',
      StartHour: 14,
      EndHour: 16
    }});

    expect(spy).toBeCalledWith({
      currentCareMoment: {
        ID: 6,
        Description: 'Avond (20-24)',
        StartHour: 20,
        EndHour: 24
      },
      previousCareMoment: {
        ID: 5,
        Description: 'Late namiddag (16-20)',
        StartHour: 16,
        EndHour: 20
      }
    });
  });
});

Method 2: Override jasmine.pp

However, during implementing this I noticed jasmine uses the function jasmine.pp for pretty printing. So I figured I could just override this by adding the following on top of my test file:

jasmine.pp = function(obj) {
  return JSON.stringify(obj, undefined, 2);
};
like image 32
Remco Haszing Avatar answered Nov 18 '22 03:11

Remco Haszing


I found that overriding jasmine.pp caused my spec reporters to no longer color-code actual vs. expected diffs.

My solution was to add the below snippet to it's own file, load it into karma.conf, then add the custom matcher (using underscore for asserting deep equality) to the config of the reporter that produces color-coded diffs in the console (karma-jasmine-diff-reporter)

//This will run before all of our specs because it's outside of a describe block
beforeEach(function() {
  var objectMatcher = {
    toEqualObject: function(util, customEqualityTesters) {
      return {
        compare: function(actual, expected) {
          var result = {};
          result.pass = _.isEqual(actual, expected);
          if (result.pass) {
            result.message = "Expected \n" + JSON.stringify(actual, null, 2) + "\n not to equal \n" + JSON.stringify(expected, null, 2) + "\n";
          } else {
            result.message = "Expected \n" + JSON.stringify(actual, null, 2) + "\n to equal \n" + JSON.stringify(expected, null, 2) + "";
          }
          return result;
        }
      };
    }
  };
  jasmine.addMatchers(objectMatcher);
});

Now I can get output like this in the console, by calling expect(foo).toEqualObject(bar):

Pretty console output

Figuring out how to make this work with jasmine spies is left as an exercise for the reader.

like image 5
Legumebo_Magezfeld Avatar answered Nov 18 '22 04:11

Legumebo_Magezfeld