Differences between Angular's $httpBackend expectPost and whenPost - angularjs

While working on some tests, I was surprised to find, that simply changing the $httpBackend.expectPost to a $httpBackend.whenPost fixed some broken tests...
Looking at the docs, it says that an expect "Creates a new request expectation.", while a when "Creates a new backend definition.". Unfortunately this doesn't mean much too me...
Can someone explain the difference between these two methods?

As mentioned in the docs,
Request expectations provide a way to make assertions about requests
made by the application and to define responses for those requests.
The test will fail if the expected requests are not made or they are
made in the wrong order.
Backend definitions allow you to define a fake backend for your
application which doesn't assert if a particular request was made or
not, it just returns a trained response if a request is made. The test
will pass whether or not the request gets made during testing.
With whenPost() definition, whenever your code makes a POST request through $http, this method will intercept and serve the response. But in case of expectPost(), it actually creates an expectation about POST request to that URL and if your code doesn't make any POST request to that URL, test will fail.
In case a request is made, then it will also respond with mock object.

Related

Adding auth token to default headers vs. using $http interceptors

I've been diving into authentication between Angular and Express, and decided on using token auth with JWTs and the npm jsonwebtoken package. I've got everything set up on the server side and am receiving the token on the client side, but now I need to know how to make it send the token with every request.
From what I've found, most resources out there say to use an $http interceptor to transform every outgoing request. But people at work have always used $httpProvider.headers.defaults.common["Auth"] = token in a .config block, which seems a lot more straightforward to me. Here's a blog explaining how to do it both ways.
But the accepted answer on this stackoverflow post says it would be better to use interceptors, but he doesn't give a reason why.
Any insight would be helpful.
After a bunch more research and a conversation on Reddit, it seems like the best way to do it is through the interceptor. Doing the setup in the .config or .run blocks may be good for checking if the user is already authenticated when they first load the app (if there is a token in local storage), but won't be possible for handling dynamic changes like logging out or logging in after the app is loaded. I'm pretty sure you could do it through the $http default headers, but might as well just do it in one place.
Hopefully this helps someone in the future!

Karma Jasmine AngularJS testing with real HTTP requests

I want to test my code with real API calls (so I can test the API as well, and when I change the API I don't have to change the JS test as well, and a lot more benefits.) instead of the regular $httpBackend.expectPOST('http://api.com/login').response(200).
Essentially, I want to test a ProductsController that expects to be logged in through an AuthService.login() method and receive a list of products through ui-router's resolve feature.
In this case, the login method receives data that needs to be used to gather products.
From the $httpBackend documentation found here: https://docs.angularjs.org/api/ngMockE2E/service/$httpBackend
As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application is being developed with the real backend api replaced with a mock, it is often desirable for certain category of requests to bypass the mock and issue a real http request (e.g. to fetch templates or static files from the webserver). To configure the backend with this behavior use the passThrough request handler of when instead of respond
So, something like: $httpBackend.whenGET(/.*/).passThrough(); should suffice.

Angularjs proceed with server call even if CORS does not allow a header value

I have an AngularJS application that talks to various java services. In the application I have a global http header setting in an http interceptor. That means all the service requests from my application will get the header values.
Now the trouble is that all the services CORS settings won't allow this header value. Couple of services does, while others does not. The service calls to the servers that do not support the header fails, since the http interceptor always puts the header values.
Is there a better way to design, in the above said case, so as to avoid the issue stated?
Appreciate any help...
How about adding a response interceptor, looking for a 401 status? If you get a 401, attempt to do the same request again without the headers this time. If this succeeds, 'whitelist' this domain to make all following requests without the headers that you don't want.
Otherwise, if you have a limited number of services that you are making calls to, maybe whitelist them inside of your request interceptor? This would probably be easier, but it's not very elegant.

Preventing similar POST requests with AngularJS

I am currently building a dashboard page with multiple widgets. Those widgets retrieve their data with REST calls ($resource). A few widgets make similar calls and I don't want to DDOS our server so I am looking for a way to make a call only once and resolve all similar requests with the same response.
Since I am restricted to using POST requests only, I cannot use the cache option that $resource offers. This seems to be doing exactly what I want but only for GET requests.
I was thinking along the lines of using a http interceptor to queue similar POST requests, fire only one of them and resolving them all when the first one gets its response.
However, I cannot seem to put the pieces together so any help is appreciated. I am open to other options.
Kind regards,
Tim
Services in AngularJS are singletons, so a solution would be to store the response in the service, as a variable. Then next time you'll do the request, previously check if the variable is null, if it's not you wrap it in a promise and returned it. If it's null, then you do the request, and store the response for the next call.
You can also either use this in your request service or in your interceptor service.
I hope I helped !
Refactor your widgets to depend on a service (singleton).
This service should either poll the server via XHR, or get server push via websocket for updates.
If polling, look into server side caching and etags.

Controlling how each catch in $httpBackend.when() is handled

I'm trying to build some jasmine unit tests around my angular project. I've run into a situation I'm not sure how best to work around.
I use a response interceptor that's capable of retrying a request if it determines the error was a 401 error. It will make a call to renew the authorization token, and then reissue the request transparently.
(Orginal Call) -> 401 ? (recert and retry) : (return fail)
My problem lies with:
$httpBackend.whenPOST('/mymockedendpoint').respond(401, null);
This is the behavior I want the FIRST time it is queried. However, because it also controls all subsequent queries, my token renewal works, and then it reissues this request, but instead of returning a 200 like it would in production, it still returns the 401.
How can I extend the handling of that whenPOST so that I can control the behavior in a handler of some sort? Is that even possible?
Right now executing my test produces an infinite loop because the both reissues the request (since it successfully renewed the token) and catches again because the would-be-200 requests returns a 401).
Instead of using the "when" based functions of $httpBackend you can use the "expect" versions. This will let you assert that a particular request is made in a particular order. The documentation for $httpBackend describes the differences pretty well:
$httpBackend.expect - specifies a request expectation
$httpBackend.when - specifies a backend definition
Request Expectations vs Backend Definitions
Request expectations provide a way
to make assertions about requests made by the application and to
define responses for those requests. The test will fail if the
expected requests are not made or they are made in the wrong order.
Backend definitions allow you to define a fake backend for your
application which doesn't assert if a particular request was made or
not, it just returns a trained response if a request is made. The test
will pass whether or not the request gets made during testing.
Given that, try this in your test:
$httpBackend.expectPOST('/mymockedendpoint').respond(401, null);
$httpBackend.expectPOST('/mymockedendpoint').respond(200, { });
$httpBackend.flush();
Also, note the function $httpBackend.resetExpectations(), which could be useful in this type of scenario.

Resources