How to mock an angular $http call and return a promise object that behaves like $http

whyceewhite picture whyceewhite · Jun 24, 2014 · Viewed 20.1k times · Source

Is there a way to return an HttpPromise (or something similar) to mimic a call to $http? I want to set a global variable that indicates whether the real HTTP request is made or whether a fake HttpPromise object is returned with fake data.

For example, I have a service that is similar to this:

angular
  .module('myservice')
  .factory('MyService', ['$http', function($http) {
      return {
       get : function(itemId) {
         if (isInTestingMode) {
           // return a promise obj that returns success and fake data
         }
         return $http.get("/myapp/items/" + itemId);
       }
    };
 } ]);

And in my controller, I have a call to the aforementioned service that looks similar to this:

        // Somewhere in my controller

        MyService.get($scope.itemId)
           .success(function(data) {
              $scope.item = data;
           })
           .error(function(data, status, headers, config) {
              $scope.notFound = true;
           });

I'm trying to not change the controller code; I want the success and error chaining to still work when in my "isInTestMode". Is it possible to fake an HttpPromise in the way that I described in the service?


Below is a revised edition of the "MyService" above (a snippet) containing a success and error on the promise object. But, how do I execute the success method?

        return {
           get : function(itemId) {
             if (isInTestingMode) {
                var promise = $.defer().promise;
                // Mimicking $http.get's success 
                promise.success = function(fn) {
                  promise.then(function() {
                     fn({ itemId : "123", name : "ItemName"}, 200, {}, {});
                  });
                  return promise;
                };
                // Mimicking $http.get's error 
                promise.error = function(fn) {
                   promise.then(null, function(response) {
                     fn("Error", 404, {}, {});
                   });
                   return promise;
                };
                return promise;
             }
             return $http.get("/myapp/items/" + itemId);
           }
        }

Answer

domokun picture domokun · Jun 24, 2014

Just use the deferred method of the $qservice

    var fakeHttpCall = function(isSuccessful) {

      var deferred = $q.defer()

      if (isSuccessful === true) {
        deferred.resolve("Successfully resolved the fake $http call")
      }
      else {
        deferred.reject("Oh no! Something went terribly wrong in you fake $http call")
      }

      return deferred.promise
    }

And then you can call your function like an $http promise (you have to customize whatever you want to put inside of it, of course).

    fakeHttpCall(true).then(
      function (data) {
        // success callback
        console.log(data)
      },
      function (err) {
        // error callback
        console.log(err)
      })