$http mocking is not persistant across tests - angularjs

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();

Related

Mock angular service data response

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
}
});

Testing the API responses format returned by angular service with Jasmine

The majority of the problems occur when the format of the interaction between me and the API changes. I want to test my angular service which talks with the API. How can I inject my angular service into test and get proper results if this service uses $http? Should I use jasmine, the tool for unit testing for this type of integration tests?
In this example I'm testing the OntologyService which uses $http and returns a promise, and the test looks like this:
describe('Service: OntologyService', function () {
var OntologyService, $scope;
beforeEach(function () {
module('oneClickRegistrationApp');
inject(function ($injector) {
OntologyService = $injector.get('OntologyService');
$scope = $injector.get('$rootScope').$new();
});
});
it('should return the object of ontologies', inject(function () {
var ontoServerApiUrl = 'https://myurl.com/api/ksearch/ontologies/';
OntologyService.getAllOntologies(ontoServerApiUrl).then(function (ontologies) {
expect(ontologies).toBeNonEmptyObject();
expect(ontologies["licenses"]).toHaveArrayOfObjects();
expect(ontologies["species"]).toHaveArrayOfObjects();
expect(ontologies["measurement_methods"].length).toBeGreaterThan(10);
});
$scope.$digest();
}));
});
I'm getting the following error message:
PhantomJS 1.9.8 (Mac OS X 0.0.0) Service: OntologyService should return the object of ontologies FAILED
Error: Unexpected request: GET https://myurl.com/api/ksearch/ontologies/hbp_data_modality_ontology?size=10000
No more request expected
at $httpBackend (/Users/katkov/WebstormProjects/one-click/bower_components/angular-mocks/angular-mocks.js:1323)
at sendReq (/Users/katkov/WebstormProjects/one-click/bower_components/angular/angular.js:10761)
at /Users/katkov/WebstormProjects/one-click/bower_components/angular/angular.js:10470
at processQueue (/Users/katkov/WebstormProjects/one-click/bower_components/angular/angular.js:14991)
at /Users/katkov/WebstormProjects/one-click/bower_components/angular/angular.js:15007
at /Users/katkov/WebstormProjects/one-click/bower_components/angular/angular.js:16251
at /Users/katkov/WebstormProjects/one-click/bower_components/angular/angular.js:16069
at /Users/katkov/WebstormProjects/one-click/test/spec/services/realontologyservice.js:32
at invoke (/Users/katkov/WebstormProjects/one-click/bower_components/angular/angular.js:4535)
at workFn (/Users/katkov/WebstormProjects/one-click/bower_components/angular-mocks/angular-mocks.js:2517)
undefined
PhantomJS 1.9.8 (Mac OS X 0.0.0): Executed 9 of 9 (1 FAILED) (0.016 secs / 0.158 secs)
Error: Unexpected request: GET https://
This blog post gets you covered: http://www.bradoncode.com/blog/2015/06/16/unit-test-http-ngmock-passthrough/
...$httpBackend service requires us to mock all HTTP requests used in the code under test...
...it would be nice to make a real HTTP call so that I can experiment, get some example JSON etc...
...ngMock does include ngMockE2E, which allows us to create fake backend HTTP calls, but we can only use this in the full application i.e. via the browser and not from unit tests...
Here is how to make real http request:
describe('real http tests', function() {
beforeEach(angular.mock.http.init);
afterEach(angular.mock.http.reset);
beforeEach(inject(function(_$controller_, _$httpBackend_) {
$controller = _$controller_;
$scope = {};
$httpBackend = _$httpBackend_;
// Note that this HTTP backend is ngMockE2E's, and will make a real HTTP request
$httpBackend.whenGET('http://www.omdbapi.com/?s=terminator').passThrough();
}));
it('should load default movies (with real http request)', function (done) {
var moviesController = $controller('MovieController', { $scope: $scope });
setTimeout(function() {
expect($scope.movies).not.toEqual([]);
done();
}, 1000);
});
});

$httpBackend doesn't respond in Protractor test

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!

AngularJS ngMock call $httpbackend.flush for nested $http

I need to write a unit test for service A, which depends on service B. Service B makes an $http calls sometimes. How can I properly call $httpBackend.flush() to get those results.
Here is the basic idea of what have:
serviceB.someMethod = function(){
var deferred = $q.defer();
someOtherCall()
.then(function(data){ deferred.resolve(data) })
.catch(function(err){
$http.get(options).success(function(data){deferred.resolve(data)}); // <-- HTTP call
});
return deferred.promise;
};
serviceA = function(things, options, filterFn){
var promises = [],
angular.forEach(things, function(thing){
promises.push(filterFn(thing, options)); // <-- filterFn calls serviceB.someMethod
});
return $q.all(promises);
};
How can I determine in my jasmine test when to call $httpBackend.flush(), so that serviceB will resolve, and thus service A? Obviously my real services have more going on, but these example should demonstrate the problem.

How to verify that an http request is not made at all?

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.

Resources