David Jones
David Jones

Reputation: 4305

Mocking Angular http response with Jasmine doesn't enter callback function

I am attempting to mock some responses from a http call from Angular in my Jasmine tests.

I have a service called 'ImageService' and this has one function called 'create'. I am spying on create and returning a rejected promise, because I am trying to test errors.

Here is my service:

this.create = function(image) {
    return imageApi.post(image);
};

The imageApi variable is a Restangular instance.

My test looks like this:

spyOn(ImageService, 'create').and.callFake(function () {
    var deferred = $q.defer();
    deferred.reject({
        data: {
            status: 400,
            errors: {
                "image.fileNotImage": "File uploaded is not an image."
            }
        }
    });
    return deferred.promise;
});

Although I am rejecting this promise it is not making its way into the error callback of my controller. The call from my controller looks like this.

ImageService.create(formData)
    .then(function(response) {
         console.log(response);
              vm.imagePreview = response;
              createAdMedia(adMedia, response.fileURL);
         }, function(error) {
              console.log(error);
              /**
              * If we get no data then we can assume its a 500 or
              * 405 error so we give a generic error message
              */
              if (error.data.errors) {
                   var errors = error.data.errors;
                   for (var key in errors) {
                        showMessage("errorMessage", DWIN.i18n.t(errors[key]));
                    }
              } else {
                    showMessage("errorMessage", DWIN.i18n.t("merchant.admedia.failure"));
              }
        });

Can anyone see an issue with this?

Thanks!

Upvotes: 1

Views: 1488

Answers (2)

Krzysztof Safjanowski
Krzysztof Safjanowski

Reputation: 7438

Some proof of concept without stubbing all stuff / methods.

angular.module('image', [])
  .service('ImageService', function($q) {
    this.create = function() {
      return $q.when();
    }
  })
  .controller('ImageCtrl', function(ImageService) {
    ImageService.create()
      .then(function(response) {}, function(error) {
        console.error('error', error);
      });
  });


describe('ImageCtrl', function() {

  var $scope;

  beforeEach(module('image'))

  beforeEach(inject(function($rootScope) {
    $scope = $rootScope.$new()
  }))

  it('ImageService.create() - supports rejection', inject(function($controller, $q, ImageService) {
    var errorMessage = {data: {error: 'some error message'}}
    spyOn(ImageService, 'create').and.returnValue($q.reject(errorMessage))
    spyOn(console, 'error')
    
    $controller('ImageCtrl', {
      $scope: $scope
    });
    
    $scope.$digest();
    
    expect(console.error).toHaveBeenCalledWith('error', errorMessage)
  }))
})
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>

Upvotes: 1

sam
sam

Reputation: 3511

You do not give the full code of the test but I suspect that you omitted to perform a $digest() or $apply() after calling your service function. Without that angular will not perform any automated feature it normally does because you are in a test environment and not the running app.

Here is an example of how you can combine $q more simply with $digest(), supposing that you are testing a controller that uses this create method :

beforeEach(inject(function ($rootScope, $controller, _$q_, _YourService_) {
    $scope = $rootScope.$new();
    $q = _$q_;
    YourService = _YourService_;

    YourController = $controller('YourController', { $scope: $scope });
}));

it('Should do something', function () {
    spyOn(YourService, 'create').and.returnValue($q.reject({data: yourdata}));

    YourController.theMethodThasUsesCreate();
    $scope.$digest();

    expect(something).toHaveBeenCalled();
});

Upvotes: 1

Related Questions