I'm trying to write a test in Protractor/Jasmine that depends upon my being able to see the headers sent in an HTTP request. To that end I'm trying to create a mock endpoint with $httpBackend that will respond to a call with the headers themselves, allowing me to look into them in my test. My code is as follows:
describe('Headers', function () {
it('should include X-XSRF-TOKEN on HTTP calls', function () {
browser.addMockModule('httpBackendMock', function () {
angular.module('httpBackendMock', ['CorsApp', 'ngMockE2E'])
.run(function ($httpBackend) {
$httpBackend.whenGET('/xsrftest')
.respond(function (method, url, data, headers) {
return headers;
});
})
});
loginPage.get();
browser.executeAsyncScript(function (callback) {
var $http = angular.injector(['ng']).get('$http');
$http.get('/xsrftest')
.then(function (response) {
callback(response);
});
})
.then(function (response) {
console.log(response);
});
});
});
I've tried to follow the patterns set out in many resources for utilizing $httpBackend in protractor testing. However, when I run this test, I get a Protractor timeout. It seems as though the $http.get call never receives a response, hence the callback is never called and so the executeAsyncScript call times out. If I put in a dummy call to the callback that's not dependent on the $http.get, it works as expected.
What am I doing wrong in setting up $httpBackend? How can I get it to respond to my $http request?
Thanks!
Related
I'm applying some tests in an existing AngularJS application in order to ensure it's correct behaviour for future changes in the code.
I am pretty new with Jasmine & Karma testing, so I've decided to start with a small and basic service which performs an http request to the backend, and waits for the result with a promise, nothing new.
Here's the service method to test:
function getInformedConsent(queryParameters) {
var def = $q.defer(),
httpParameters = {
url: ENV.apiEndpoint + '/urlResource',
method: 'GET',
params: queryParameters,
paramSerializer: '$httpParamSerializerJQLike'
};
$http(httpParameters)
.then(
function (response) {
def.resolve(response);
},
function (error) {
def.reject(error);
}
);
return def.promise;
}
And here my test:
it('getInformedConsent method test', function() {
$httpBackend.expectGET(/.*\/urlResource?.*/g)
.respond(informedConsentJson.response);
var promise;
promise = InformedconsentService.getInformedConsent(informedConsentJson.queryParameters[0]);
promise
.then(function(response) {
console.log(response);
expect(response).toEqual(informedConsentJson.response);
});
$httpBackend.flush();
});
informedConsentJson as you can supose, is a fixture with input and the expected output.
Reading AngularJS documentation, I decided to use $httpBackend, because it's already a mock of $http service, so I thought it could be useful.
The problem is that somewhere in the code, someone is broadcasting a "$locationChangeStart" event and executing
$rootScope.$on('$locationChangeStart', function (event,current,old) {
/* some code here */
});
in app.js.
I'm not trying to change the URL, i'm just trying to get some data from the mocked backend.
I asume that is because I'm not using $http mock ($httpBackend) as it should be used.
Anyone can help me with $http with configuration JSON mock?
It's freaking me out.
Thank you all in advance for your time and your responses
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 have a service that calls a REST URL that returns data.
My sample code would be
$http('POST', '/mockUrl/resource/'+resourceValue).then(...);
My service works fine and it returns data. My problem is how do I test this in karma. Right now I have a different resourceValue to be tested for the mockUrl being called. Before coming to stackoverflow, in each test i was defining $httpBackend with the expected URL.
eg:
it('testing for status 200', function(){
$httpBackend.when('POST', '/mockUrl/resource/'+1234)
.respond(function (method, url, data, headers) {
return [200, data1];
});
MyService.serviceMethod(1234).then(function(){
//test the returned data
});
});
it('testing for status 201', function(){
$httpBackend.when('POST', '/mockUrl/resource/'+4567)
.respond(function (method, url, data, headers) {
return [201, data2];
});
MyService.serviceMethod(1234).then(function(){
//test the returned data
});
});
I am being told that I should not be writing my karma tests in the above manner. But I am not sure how to avoid that.
I have tried
$httpBackend.when('POST',url)
.respond(function (method, url, data, headers) {
return data[option];
});
But this 'url' never gets called in any test. I am not sure how to proceed further.
it seems that you never call $httpBackend.flush(); that is needed to simulate async operations... Plus, you also never use
afterEach(() => {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
Probably you need to have a more in-deep view at https://docs.angularjs.org/api/ngMock/service/$httpBackend
Is this set up for $http mocking?
For some reason I am getting this error:
Uncaught Error: Unexpected request: GET http://
describe('DataService tests', function () {
var errorUrl = "/ErrorReturningURL";
var successUrl = "/SuccessReturningURL";
beforeEach(angular.mock.module('app'));
beforeEach(angular.mock.inject(function ($httpBackend) {
$httpBackend.when('GET', successUrl).respond('all good!');
$httpBackend.when('GET', errorUrl).respond(404, '');
}));
it('should call the callbackError when http returns error', inject(function (DataService, $httpBackend) {
var successCallback = jasmine.createSpy();
var errorCallback = jasmine.createSpy();
$httpBackend.expectGET(errorUrl);
DataService.getData(errorUrl, successCallback, errorCallback);
$httpBackend.flush();
expect(errorCallback).toHaveBeenCalled();
}));
}
)
;
service(simplified):
app.service('DataService', function ($http, $parse) {
this.getData = function (url, callbackSuccess, callbackError) {
$http.get(url).success( function (data) {
callbackSuccess( processedData );
}).error( function (error) {
callbackError(error);
});
};
});
original $http ?
I assume you included angular-mocks.js in your karma.js.conf file.
angular-mocks overrides the original $httpBackend , so it is impossible to do real requests.
$httpBackend mock has a synchronous API but it must integrate with your asynchronous application.
The flush() method is the connecting link between asynchronous applications and synchronous tests.
From $httpBackend docs:
Flushing HTTP requests
The $httpBackend used in production, always responds to requests with responses asynchronously. If we preserved this behavior in unit testing, we'd have to create async unit tests, which are hard to write, follow and maintain. At the same time the testing mock, can't respond synchronously because that would change the execution of the code under test. For this reason the mock $httpBackend has a flush() method, which allows the test to explicitly flush pending requests and thus preserving the async api of the backend, while allowing the test to execute synchronously
You must call flush() to actually make the request:
$httpBackend.expectGET(errorUrl);
DataService.getData(errorUrl, successCallback, errorCallback);
$httpBackend.flush();
expect(errorCallback).toHaveBeenCalled();
how to verify that none of http request method are invoked to do any request. I have this code :
$scope.getSubnetsPageDetails = function (pageNumber) {
$http.get(URLS.subnetsPagesCount(pageNumber)).success(function (response) {
$scope.pageDetails = response;
}).error(function (response, errorCode) {
});
};
and this test :
it("should not allow request with negative page number", function () {
scope.getSubnetsPageDetails(-1);
//verify that htt.get is not invoked at all
});
How to verify that http.get is not invoked ?
You can test that no calls are made by using the verifyNoOutstandingRequest() method from the $httpBackend mock.
Usually those kind of verification is done in the afterEach section of a Jasmine's tests. On top of this it is common to call another method, verifyNoOutstandingExpectation() to verify that all the expected calls were actually invoked.
Here is the code, where you need to inject the $httpBackend mock:
var $httpBackend;
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
}));
then do you test and at the end:
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
Of course you could invoke the $httpBackend.verifyNoOutstandingRequest() inside an individual test. The mentioned http://docs.angularjs.org/api/ngMock.$httpBackend page has a wealth of information on the topic.