I'm trying to test some services that receive some modifications. Some of them are using $http service, and only one of them is populating an unknown - and a not understandable - issue.
Let me expose.
it('must reject the promise with an explanation if the required path is not found', function() {
$httpBackend.whenGET('http://localhost/testok').respond(function () {
return [200, mockedRemoteResponse, {}];
});
var promise = apiDataExtractor.extractRemoteData('ok', 'toto');
$httpBackend.flush();
});
Running this code throught Jasmine, we got this:
I do not have ANY idea of what appends. I try to change injection order, try to erase and rewrite my test, there is something here I'm missing.
Can anyone help?
The error you are seeing is probably due to a call to $http.get with undefined as the url (you might have forgotten to provide parameters to $http.get() inside apiDataExtractor.extractRemoteData?)
Try debugging your code and see what are the parameters you are giving the $http.get method.
Related
I've been trying to setup a timeout value (in milliseconds) for my $resource so that if the server doesn't respond within 10*1000 milliseconds, I want the service in my factory to return the error promise.
I have a factory with the following method:
service.getDelta = $resource(BASE_API_URL + '/delta/:inTable/:inDate',
{inTable: '#inTable', inDate: '#inDate'},
{query: {timeout: 10*1000}});
In the controller I do the following:
getDataPromises.push(remoteService.getDelta.query({inTable: "table1", inDate: clientDateLastModified}).$promise);
getDataPromises.push(remoteService.getDelta.query({inTable: "table2", inDate: clientDateLastModified}).$promise);
getDataPromises.push(remoteService.getDelta.query({inTable: "table3", inDate: clientDateLastModified}).$promise);
getDataPromises.push(remoteService.getDelta.query({inTable: "table4", inDate: clientDateLastModified}).$promise);
Then I check to ensure all are successful or handle error if one fails.
$q.all(getDataPromises).then(
function (result) {
// NOTIFY SUCCESS
},
function (error) {
// NOTIFY ERROR
});
The success in the promise has been coming through as expected. If I leave out the timeout config, I also get the error in the promise for issues like 404. But when I add the timeout, I get the following when I stringify the error object in my error callback:
{"line":13380,"column":32,"sourceURL":"file:///Users/ ... /www/lib/ionic/js/ionic.bundle.js"}
Am I misunderstanding the use of the timeout property?
I am using ionic 1.2.4 which I believe is using Angular 1.4.3.
Note: I know this use case doesn't justify use of $resource and could be done with $http, but I wanted to get to get a feel for using it since I plan to down the road for this app.
Thanks!
All my UNIT tests, not E2E tests, that do an explicit rootScope.digest() or httpBackend.flush() to flush the asynchronous callback, experience the error:
How to avoid the 'Error: Unexpected request: GET'
No more request expected
I reckon it is because httpBackend calls the ui-router template. I don't know why it wants to do so. I'm not asking for this. I only want it to call my mocked json service.
This error forces me to have the following statement in each it() block:
$httpBackend.whenGET(/\.html$/).respond('');
There must be a neater way.
Specially if the test has no use of the $httpBackend in the first place:
it('should return the list of searched users', function() {
// Always use this statement so as to avoid the error from the $http service making a request to the application main page
$httpBackend.whenGET(/\.html$/).respond('');
var users = null;
UserService.search('TOTO', 1, 10, 'asc', function(data) {
users = data.content;
});
$rootScope.$digest();
expect(users).toEqual(RESTService.allUsers.content);
});
The test passes but it looks hackish. Or noobish :-)
EDIT: Another test:
it('should return the list of users', function () {
// Always use this statement so as to avoid the error from the $http service making a request to the application main page
$httpBackend.whenGET(/\.html$/).respond('');
// Create a mock request and response
$httpBackend.expectGET(ENV.NITRO_PROJECT_REST_URL + '/users/1').respond(mockedUser);
// Exercise the service
var user = null;
RESTService.User.get({userId: 1}).$promise.then(function(data) {
user = data;
});
// Synchronise
$httpBackend.flush();
// Check for the callback data
expect(user.firstname).toEqual(mockedUser.firstname);
expect(user.lastname).toEqual(mockedUser.lastname);
});
This is obviously by design, your tests should be checking that HTTP calls are being made and that they're requesting the correct URL. Instead of checking whether requests are made to /\.html$/ why not instead check whether requests are made to the correct endpoints? Whether that be a directives partial or an API call.
If you insist on throwing away what could be a useful test, you could move your whenGET() to a beforeEach().
Hello I have following code
myApp.controller('myctrl',function($scope,myservice){
//where testClickEvent is ng-click from partials
$scope.testClickEvent=function(args){
var setArg1=args[0];
var params = {"myarg1":setArg1}
//rest call
myservice.testClickEvent(params).success(function(data){
if(data.res==true)
{
$scope.somevariable="success";
}
}).error(function(error){
$scope.somevariable="failure";
});
}
});
I wanted to test it using jasmine that is
testing ng-click event happened in partial ??
how do i test $scope.somevariable and setArg1 has got proper value
here i have used
spyOn(scope,testClickEvent);
scope.testClickEvent(args);
expect(scope.testClickEvent).toHaveBeenCalledWith(args);
spyOn(myservice,testClickEvent);
myservice.testClickEvent(params);
expect(myservice.testClickEvent).toHaveBeenCalledWith(params);
works but how to access result and async call back result????
Thanks in advance ! Your Help appreciated !
You would need to mock out the call using $httpBackend
https://docs.angularjs.org/api/ngMock/service/$httpBackend
The example from the angular page has the following 2 lines:
$httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'}); $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
The first one mocks out the response and responds with whatever object is specified
The second one expects the server to be called with a specified object.
Hope this helps.
I have a Jasmine test that is coded like this:
it ("should send correct message to server to get data, and correctly set up scope when receiving it", function(){
$httpBackend.when('GET', 'https://localhost:44300/api/projectconfiguration/12').respond(fakedDtoBase);
$routeParams.projectId=fakeId; // user asks for editing project
scope.$apply(function(){
var controller=controllerToTest(); // so controller gets data when it is created
});
expect(scope.projectData).toEqual(fakedDtoBase);
});
and it kind of works, but I get the error:
Error: Unexpected request: GET views/core/main/main.html
No more request expected
at $httpBackend (C:/SVN/src/ClientApp/client/bower_components/angular-mocks/angular-mocks.js:1207:9)
at sendReq (C:/SVN/src/ClientApp/client/bower_components/angular/angular.js:7800:9)
at $http.serverRequest (C:/SVN/src/ClientApp/client/bower_components/angular/angular.js:7534:16)
(more stack trace)....
I do realise that I can mock every other call. But let's say I do not care what else my test wants to load as it may call few other things.
How I can make sure that every other requests just "happen silently", maybe offering a single dummy response for everything else?
Your test fails because a request is made which you haven't specified.
Try to add:
$httpBackend.when('GET', 'views/core/main/main.html').respond(fakedMainResponse);
Of course you should also define fakedMainResponse.
Please take a look also at the documentation (section Request Expectations vs Backend Definitions) which says:
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.
The second paramete of $httpBackend.when is actually a RegExp. So if you provide a RegExp that will match all other requests it should work.
For those who are using the httpBackend to mock http calls in EndToEnd tests or just mocking the entire http calls for the application the solution is to add the following code in the app config section (change the regexp according your template's location):
$httpBackend.whenGET(/^\/templates\//).passThrough();
Reference: https://docs.angularjs.org/api/ngMockE2E/service/$httpBackend
Tested with angularjs 1.4 to fix similar problem while integrating ui-router
I think it's also important to notice that if you have a $digest(), your expectation should follow the $digest, like so:
_$rootScope_.$digest();
$httpBackend.when('GET', 'views/core/main/main.html').respond(fakedMainResponse);
// ...
$httpBackend.flush(); // And remember to flush at the end
you only need to add setTimeout and done property to your flush to prevent it
it('should get data in callback funcion', function (done) {
$httpBackend.whenGET(/\/my-endpoint/).respond(mockDataResponse);
apiFactory.getCurrencyFormat('en', function (res, err) {
expect(res.a).to.deep.equal(generalMock.a);
expect(res.b).to.deep.equal(generalMock.b);
});
setTimeout(function () {
done();
$httpBackend.flush();
}, 200);
});
I am using AngularJS Services in my application to retrieve data from the backend, and I would like to make a loading mask, so the loading mask will start just before sending the request. but how can I know when the request ends?
For example I defined my servive as:
angular.module('myServices', ['ngResource'])
.factory('Clients', function ($resource) {
return $resource('getclients');
})
.factory('ClientsDetails', function ($resource) {
return $resource('getclient/:cltId');
})
So I use them in my controller as:
$scope.list = Clients.query();
and
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
});
So the question would be, how to know when the query and get requests ends?
Edit:
As a side note in this question I've been using using angularjs 1.0.7
In AngularJS 1.2 automatic unwrapping of promises is no longer supported unless you turn on a special feature for it (and no telling for how long that will be available).
So that means if you write a line like this:
$scope.someVariable = $http.get("some url");
When you try to use someVariable in your view code (for example, "{{ someVariable }}") it won't work anymore. Instead attach functions to the promise you get back from the get() function like dawuut showed and perform your scope assignment within the success function:
$http.get("some url").then(function successFunction(result) {
$scope.someVariable = result;
console.log(result);
});
I know you probably have your $http.get() wrapped inside of a service or factory of some sort, but you've probably been passing the promise you got from using $http out of the functions on that wrapper so this applies just the same there.
My old blog post on AngularJS promises is fairly popular, it's just not yet updated with the info that you can't do direct assignment of promises to $scope anymore and expect it to work well for you: http://johnmunsch.com/2013/07/17/angularjs-services-and-promises/
You can use promises to manage it, something like :
Clients.query().then(function (res) {
// Content loaded
console.log(res);
}, function (err) {
// Error
console.log(err);
});
Another way (much robust and 'best practice') is to make Angular intercepting your requests automatically by using interceptor (see doc here : http://docs.angularjs.org/api/ng.$http).
This can help too : Showing Spinner GIF during $http request in angular
As left in a comment by Pointy I solved my problem giving a second parameter to the get function as following:
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
}, function(){
// do my stuff here
});