I have an Api service which is in charge of controlling all my http requests. GET, POST, PUT, DELETE...
I'm trying to write some unitTests and I get a problem with the following scenario.
self.Api.post('/myEndpoint/action/', actionData)
.then(function(resp){
result = _.get(resp, 'data.MessageList');
if(resp.status = 200 && result) {
setActionResults(resp.data);
}
});
I want to mock in my unitTest the resp. What should I do? Must I mock the httpBackend service as here http://plnkr.co/edit/eXycLiNmlVKjaZXf0kCH?p=preview ? Can I do it in other way?
Using httpBackend is the way to go, mocking each request made by your application will work just fine. However you can mock your entire service as well, and unit test using the mocked service instead of the original. Regardless, httpBackend is much more simple to handle that (for http request services) than creating a new service with the same interface of the original. But in some case, you may need to control what your services are doing, therefore you will have to use service mocking.
For example:
angular.module('myApp')
.service('DataService', function ($http) {
this.getData = function () {
return $http.get('http://my.end.point/api/v1/data')
.then(function (response) {
return response.data;
});
};
});
angular.module('myAppMock')
.service('MockedDataService', function ($q) {
this.getData = function () {
return $q.resolve({ data: 'myData' }); // you can add a delay if you like
}
});
I'm trying to call a web api POST method using angular js it get call.
I try from postman utility it also get call and in respose give a json data.But in angular side i am not getting data in response.
my services in angular js
var _getProjects = function (searchText) {
debugger;
return $http.post(URL + 'api/project/GetProjectDetails?searchText='+ searchText).then(function (response) {
return response;
});
};
and below is my web api
[HttpPost]
[Route("api/project/GetProjectDetails")]
public List<GetKeywordsBasisData_Result> GetProjectDetails(string searchText)
{
if (!String.IsNullOrEmpty(searchText))
{
return projectProvider.GetProjectDetails(searchText).ToList();
}
return new List<GetKeywordsBasisData_Result>();
}
You are missing a callback. Http Calls on $http happen asynchronously, you need to pass in a method to call once the call completes. Something like this. The parameter myCallback is a function that takes in the expected response, you can then process it (or whatever) inside that function.
function (searchText, myCallback) {
$http.post(URL + 'api/project/GetProjectDetails?searchText='+ searchText).then(function (response) {
myCallback(response);
});
};
You can then call it from your controller like this assuming getProjects is your factory method. Also assuming that $scope is a field on your controller.
var me = this;
getProjects('somethingToSearchFor', function(results){
me.$scope.results = results;
});
I have a single page angular app which calls a RESTish service. How can I configure the base URL for my REST server so that in my services I can use relative URLs? Also, as I was playing around with the following interceptor I got an error where angular-ui router seemed to be using http to get views so this was affected by the middleware. Basically I guess I want a second http service to inject into my services that has this middleware, how can I do this?
app.config(["$httpProvider", function($httpProvider) {
$httpProvider.interceptors.push('middleware');
}]);
app.factory('middleware', function() {
return {
request: function(config) {
// need more controlling when there is more than 1 domain involved
config.url = "http://localhost:8080" + config.url;
return config;
}
};
});
The $http services is very important (and widely used) inside Angular.
You shouldn't alter it like this.
For handling requests to an API, it is best to create a dedicated service (even if it's a wrapper around $http).
E.g.:
.service('API', function ($http) {
var baseUrl = 'http://localhost:8080/';
this.get = function (path, config) {
return $http.get(baseUrl + path, config);
}
...
});
Then you can use your service for all API calls:
.controller('someCtrl', function (API) {
API.get('items').success(function (data) {
$scope.items = data;
});
});
I have some angular factories for making ajax calls towards legacy ASP.NET .asmx web services like so:
module.factory('productService', ["$http",
function ($http) {
return {
getSpecialProducts: function (data) {
return $http.post('/ajax/Products.asmx/GetSpecialProducs', data);
}
}
} ]);
I'm testing on a local network so response times are "too" good. Is there a smart way of delaying the $http a couple of seconds from making the call to simulate a bad connection?
Or do I need to wrap all calls to the factory methods in a $timeout ?
$timeout(function() {
productService.getSpecialProducs(data).success(success).error(error);
}, $scope.MOCK_ajaxDelay);
Interesting question!
As you mentioned yourself, $timeout is the most logical choice for a delayed call. Instead of having $timeout calls everywhere, you could push a response interceptor that wraps the $http promise in a $timeout promise, as conceptually outlined in the documentation of $http, and register it in one of your configuration blocks. This means all $http calls are affected by the $timeout delay. Something along the lines of:
$httpProvider.interceptors.push(function($timeout) {
return {
"response": function (response) {
return $timeout(function() {
return response;
}, 2500);
}
};
});
As a bonus to your "to simulate a bad connection?", you could reject or do absolutely nothing randomly, too. Heh heh heh.
The new chrome device emulator has a network throttling function:
To get there: In Google Chrome, press F12 to open the Developer Tools. Then, on the top left corner, click the "Toggle device mode" icon (left to the "Elements" menu).
Developing more on the answer of #stevuu
responseInterceptors seems to be depreceted (as of 1.2.20) I have modified the code to work on the interceptors mechanism:
$httpProvider.interceptors.push(function($q, $timeout) {
return {
'response': function(response) {
var defer = $q.defer();
$timeout(function() {
defer.resolve(response);
}, 2300);
return defer.promise;
}
};
});
You could use the $q service for defer().promise pattern:
function someFunction(MOCK_ajaxDelay) {
var deferred = $q.defer();
$http.post('/ajax/Products.asmx/GetSpecialProducs', data).success(function(response) {
$timeout(function() {deferred.resolve({ success: true, response: response })}, MOCK_ajaxDelay);
}).error(function() {
$timeout(function() {deferred.resolve({ success: true, response: response } }, MOCK_ajaxDelay);
});
return deferred.promise;
}
someService.someFunction(500).then(function(data) {
if (data.success) {
$scope.items = data.response.d;
}
});
But if you are really mock testing, the better solution is to look into ngMock: http://docs.angularjs.org/api/ngMock.$httpBackend
While #stevuu's answer is correct, the syntax has changed in the newer AngularJS versions since then. The updated syntax is:
$httpProvider.interceptors.push(["$q", "$timeout", function ($q, $timeout) {
function slower(response) {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve(response);
}, 2000);
return deferred.promise;
}
return {
'response': slower
};
}]);
You can achieve this using the promise api combined with a $timeout. The $http.post function returns a promise from which you can call .success and .error (these are http specific methods). This promise is resolved when the http request is complete. If you build your own promise then you can tell it to delay 2 seconds and then resolve when the http request is complete:
module.factory('productService', function ($http, $q, $timeout) {
return {
getSpecialProducts: function (data) {
var defer = $q.defer();
$http.post('/ajax/Products.asmx/GetSpecialProducs', data).success(
function(data) {
// successful http request, resolve after two seconds
$timeout(function() {
defer.resolve(data);
}, 2000)
}).error(function() {
defer.reject("Http Error");
})
return defer.promise;
}
}
});
But note - you will have to use promise.then(successCallback, errorCallback) functionality - that is, you'll lose the ability to access http headers, status & config from your controllers/directives unless you explicitly supply them to the object passed to defer.resolve({})
Links:
Defer/Promise Api
Http/Promise Api
Resolve egghead video
In response to the testing aspect of your question, Fiddler has a really useful function that helps when you need to simulate delays:
Click on the AutoResponders tab in Fiddler.
Add a rule with a regex that matches the URL of the request you want to delay.
Set the "respond with" to "*delay:1000" where the number is the delay in milliseconds.
The AutoResponder functionality in Fiddler is extremely useful for testing JS that involves a lot of http requests. You can set it to respond with particular http error codes, block responses, etc.
If you are using a service that returns a promise, then inside you should put a return before the $timeout as well because that returns just another promise.
return dataService.loadSavedItem({
save_id: item.save_id,
context: item.context
}).then(function (data) {
// timeout returns a promise
return $timeout(function () {
return data;
},2000);
});
Hope it helps someone!
Could you explain in general what does this code do:
App.config(['$httpProvider', function ($httpProvider) {
$httpProvider.responseInterceptors.push('HttpSpinnerInterceptor');
$httpProvider.defaults.transformRequest.push(function (data, headersGetter) {
angular.element('.brand img').attr("src","<%= asset_path('brand/brand.gif') %>");
return data;
});
}]);
App.factory('HttpSpinnerInterceptor', function ($q, $window) {
return function (promise) {
return promise.then(function (response) {
angular.element('.brand img').attr("src","<%= asset_path('brand/brand.png') %>");
return response;
}, function (response) {
angular.element('.brand img').attr("src","<%= asset_path('brand/brand.png') %>");
return $q.reject(response);
});
};
});
I have completely no understanding except some guesses that it intercepts some response and injects a src attribute of image.
I do not understand how and when is HttpSpinnerInterceptor called and what the "promise" parameter is.
HttpSpinnerInterceptor is been called after each request issued by using $http service is completed (successfully or not), but before promise is been resolved to caller (so you can defer result). Actually transform request is not needed, because it does mostly same as HttpSpinnerInterceptor (or HttpSpinnerInterceptor is not needed...), because it does not transform anything.
promise parameter is a $q promise that could be used in case if you need to perform some async actions when with result of your request as you can resole it later, so caller would get result later. Actually in your code, you directly resolve this promise (or reject it), changing src attribute of the image.
Here are some links to documentation:
Using $http service: http://docs.angularjs.org/api/ng.$http - take careful look at "Response interceptors" and "Transforming Requests and Responses"
Promises in AngularJS: http://docs.angularjs.org/api/ng.$q