How to Karma test Restangular http call using httpbackend to test both success and error cases?

874 views Asked by At

I am testing an angular service using karma/jasmine and one of my service functions is as follows. I need to get coverage to 100%, but can't seem to figure out how to test both success and error cases..

function getAccount(accountId) {
   var defer = $q.defer(), myService;
   myService = Restangular.all('Some/Url/Path');
   myService.get('', {}, {
     'header-info': 'bla'
   })
   .then(function onSuccess(response) {
      defer.resolve(response);
   }, function onError() {
      someMethodCall();
   });
   return defer.promise;
}

In my corresponding .spec test file, I have:

it('should succeed in getting account', function() {
   httpBackend.whenGET('Some/Url/Path').respond(200, mockResponse);
   var promise = myServices.getAccount('account123');
   promise.then(function(response) {
     expect(response).toEqual(mockResponse);
});

it('should error out in getting account', function() {
   httpBackend.whenGET('Some/Url/Path').respond(500, '');
   var promise = myServices.getAccount('account123');
   promise.then(function() {
     expect(someMethodCall).toHaveBeenCalled();
});

Right now, both cases "pass", but I'm not getting the branch coverage for the onError case. Something seems fishy about the onSuccess case passing too.

Basically I am asking what is the correct syntax and way of writing the test cases such that I can hit both success and on error cases when I make a 200 and a 500 call to my API

1

There are 1 answers

1
tasseKATT On BEST ANSWER

Since you don't have any calls to $http in your service, I would recommend mocking Restangular instead of using httpBackend. This way your test doesn't have to know anything about the implementation details of Restangular, other than what it returns, just like your service.

Mock example:

var Restangular = {
  all: function() {
    return {
      get: function() {
        restangularDeferred = $q.defer();
        return restangularDeferred.promise;
      }
    };
  }
};

Now you can easily either resolve or reject restangularDeferred depending on what you want to test.

Set up your module to use the mock:

module('myApp', function($provide) {

  $provide.value('Restangular', Restangular);
});

Example test of success case:

it('success', function() {

  // If you want you can still spy on the mock
  spyOn(Restangular, 'all').and.callThrough();

  var mockResponse = {};

  var promise = myServices.getAccount('account123');

  promise.then(function(response) {

    expect(response).toEqual(mockResponse);
    expect(Restangular.all).toHaveBeenCalledWith('Some/Url/Path');
  });

  restangularDeferred.resolve(mockResponse);

  // Trigger the digest loop and resolution of promise callbacks
  $rootScope.$digest();
});

Example test of error case:

it('error', function() {

  spyOn(anotherService, 'someMethodCall');

  var mockResponse = {};

  myServices.getAccount('acount123');

  restangularDeferred.reject(mockResponse);

  $rootScope.$digest();

  expect(anotherService.someMethodCall).toHaveBeenCalled();
});

Note that I moved someMethodCall into anotherService in the example.

Demo: http://plnkr.co/edit/4JprZPvbN0bYSXFobgmu?p=preview