0

This is a test for a $resource with a loader

describe('Service: MultiCalculationsLoader', function(){

  beforeEach(module('clientApp'));

  var MultiCalculationLoader,
    mockBackend,
    calculation;

  beforeEach(inject(function (_$httpBackend_, Calculation, _MultiCalculationLoader_) {
    MultiCalculationLoader = _MultiCalculationLoader_;
    mockBackend = _$httpBackend_;
    calculation = Calculation;
  }));

  it('should load a list of calculation from a user', function(){
    mockBackend.expectGET('/api/user/600/calculation').respond([{id:5}]);

     var calculations;
     var mockStateParams = {
       userId: 600
     };
    var promise = new MultiCalculationLoader(mockStateParams);

    promise.then(function(res){
      calculations = res
    });

    expect(calculations).toBeUndefined();

    mockBackend.flush();

    expect(calculations).toEqual([{id:5}]);
  });

});

When I run the test I get the following error:

Expected [ { id : 5 } ] to equal [ { id : 5 } ].
Error: Expected [ { id : 5 } ] to equal [ { id : 5 } ].
    at null.<anonymous> 

I don't get it. The two arrays are the same to me. Ideas anyone?

Update Here's the implementation:

 .factory('Calculation', function ($resource) {
    return $resource('/api/user/:userId/calculation/:calcId', {'calcId': '@calcId'});
  })
  .factory('MultiCalculationLoader', function (Calculation, $q) {
    return function ($stateParams) {
      var delay = $q.defer();
      Calculation.query( {userId: $stateParams.userId},function (calcs) {
        delay.resolve(calcs);
      }, function () {
        delay.reject('Unable to fetch calculations');
      });
      return delay.promise;
    };
  })
Joe
  • 4,274
  • 32
  • 95
  • 175

3 Answers3

2

The url you expect is different from the actual url. I guess you need something like this:

it('should load a list of calculation from a user', function(){
    //remove the 's' from 'calculations'
    mockBackend.expectGET('/api/user/600/calculation').respond([{id:5}]);

    var calculations;
    var promise = MultiCalculationLoader({userId:600}); //userId = 600
    promise.then(function(res){
      calculations = res
    });

    expect(calculations).toBeUndefined();

    mockBackend.flush();

    expect(calculations).toEqual([{id:5}]);
  });

There is another problem that angular automatically adds 2 properties to the response:

enter image description here

http://plnkr.co/edit/gIHolGd85SLswzv5VZ1E?p=preview

It's kind of the same problem as: AngularJS + Jasmine: Comparing objects

This is really a problem with angular $resource when angular convert the response to resource objects. In order to verify responses from $resource, you could try angular.equals

expect(angular.equals(calculations,[{id:5},{id:6}])).toBe(true);

http://plnkr.co/edit/PrZhk2hkvER2XTBIW7yv?p=preview

You could also write a custom matcher:

beforeEach(function() {
    jasmine.addMatchers({
      toEqualData: function() {
        return {
          compare: function(actual, expected) {
            return {
              pass: angular.equals(actual, expected)
            };
          }
        };
      }
    });
  });

And use it:

expect(calculations).toEqualData([{id:5},{id:6}]);

http://plnkr.co/edit/vNfRmc6R1G69kg0DyjZf?p=preview

Community
  • 1
  • 1
Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • Isn't it a bit messy? Maybe I'm totally off here but how about using angular.toJson? I think that removes the "junk" from the array? – Joe Aug 27 '14 at 18:06
  • @user1572526: I found out that we could use `angular.equals` for that. `angular.equals` will strip out functions and internal implementation details of angular (like hashkey) when testing – Khanh TO Aug 28 '14 at 11:01
  • @user1572526: you could also write a custom matcher. See updated answer – Khanh TO Aug 29 '14 at 13:29
0

Jasmine equality selectors can be too specfic sometimes when you just wanna check for equaity.

I have never seen it with the toEqual() method when comparing objects or arrays, but def with the toBe() method.

Try to replace toEqual() with toMatch().

Also in the unit test I recommend using a constant value that you can pass in the response and the matchers/equal/toBe's.

describe('Service: MultiCalculationsLoader', function(){

  beforeEach(module('clientApp'));

  var MultiCalculationLoader,
    mockBackend,
    calculation,
    VALUE = [{id:5}];

  beforeEach(inject(function (_$httpBackend_, Calculation, _MultiCalculationLoader_) {
    MultiCalculationLoader = _MultiCalculationLoader_;
    mockBackend = _$httpBackend_;
    calculation = Calculation;
  }));

  it('should load a list of calculation from a user', function(){
    mockBackend.expectGET('/api/user/600/calculations').respond(VALUE);

    var calculations;
    var promise = MultiCalculationLoader();
    promise.then(function(res){
      calculations = res
    });

    expect(calculations).toBeUndefined();

    mockBackend.flush();

    expect(calculations).toEqual(VALUE);
  });

});

Using this approach I think the .toEqual will actually work.

Our approach:

Before Block:

httpBackend.when('JSONP', PATH.url + 'products?callback=JSON_CALLBACK&category=123').respond(CATEGORIES[0]);

Test:

describe('Category Method', function () {

        it('Should return the first category when the method category is called', function () {
            var result = '';

            service.category(123).then(function(response) {
                result = response;
            });

            httpBackend.flush();
            expect(result).toEqual(CATEGORIES[0]);

        });
    });
Sten Muchow
  • 6,623
  • 4
  • 36
  • 46
0

you can try change toEqual() with toEqualData()

expect(calculations).toEqualData([{id:5}]);
ikhsannetwork
  • 103
  • 2
  • 6