I have a call to service's function in a controller. Below is the code
Service
(function () {
'use strict';
angular.module('MyApp')
.service('MyService', ['$http', function ($http) {
return {
getMyData: function (extension) {
return $http.get('www.something.com');
}
};
}])
})();
Controller
var getMyData = function () {
MyService.getMyData(extension).success(function (results) {
//Some functionality here
})
.error(function (err, status) {
//Some functionality here
});
}
$scope.Call=function(){
getMyData();
}
$scope.Call();
Now please tell me how to mock the service call (may be with providers). How to test the above functions with complete code coverage.
My spec file:
$provide.service("MyService", function () {
this.getMyData= function () {
var result = {
success: function (callback) {
return callback({ ServerFileName: "myserverfilename"});
},
error: function (callback) {
return callback({ ServerFileName: "myserverfilename" });
}
};
return result;
}
//......
my controller initiation and other code
This code is not covering error block and giving the error
Cannot read property 'error' of undefined
Please help me how to write/mock the getMyData function of my service in my spec file
Thanks in advance.
Since .success
and .error
are old and have been replaced with .then(successCallback, errorCallback)
, you should consider replacing your chained .success
and .error
calls with a single call to the .then
method with two callbacks as arguments to it: first being a success callback and second being an error callback.
If that's what you're willing to do, here's your working example:
You Module, Service and Controller
angular.module('MyApp', []);
angular.module('MyApp')
.service('MyService', ['$http', function ($http) {
return {
getMyData: function (extension) {
return $http.get('www.something.com');
}
};
}]);
angular.module('MyApp')
.controller('MyAppController', ['$scope', function($scope){
var extension = { foo: 'bar' };
var getMyData = function () {
MyService.getMyData(extension).then(function (results) {
//Some functionality here
}, function (err, status) {
//Some functionality here
});
}
$scope.Call=function(){
getMyData();
}
$scope.Call();
}]);
And your Test
describe('Controller: MyAppController', function(){
beforeEach(module('MyApp'));
var flag, extension, $q;
extension = { foo: "bar" };
beforeEach(inject(function($controller, $rootScope, _MyService_, _$q_) {
$scope = $rootScope.$new();
MyService = _MyService_;
$q = _$q_;
spyOn(MyService, 'getMyData').and.callFake(function(){
return flag ? $q.when(): $q.reject();
});
MyAppController = $controller('MyAppController', {
$scope: $scope,
MyService: MyService
});
}));
describe('function: Call', function() {
//Text for Success Callback
it('should implicitly call MyService.getMyData with an extension object', function() {
flag = true;
$scope.Call();
expect(MyService.getMyData).toHaveBeenCalledWith(extension);
});
//Text for Error Callback
it('should implicitly call MyService.getMyData with an extension object', function() {
flag = false;
$scope.Call();
expect(MyService.getMyData).toHaveBeenCalledWith(extension);
});
});
});
UPDATE:
I've tried making something like this to work but with no luck. Since .error()
's call is chained to .success()
call, and that is something that will get called only after .success()
has been called, it will never get to .error()
's call and we'll not be able to mock .error()
. So if we try doing that, we'll always get an error like:
Cannot read property 'error' of undefined
So either you can use the comment /*istanbul ignore next*/
to skip this part in the coverage, or switch to .then()
.
Hope this helps.
You need to use spyon which would create some sort of mock for your service. You need to do this in your test file. Please check the below code:
spyOn(MyService, "getMyData").and.callFake(() => {
return {
error: (callback) => {
return callback({});
}
};
});
Hope i answered your question
Here is the solution. I had also encountered similar issue. Look like we have to design our own code and Jasmine allows us to design, customize the callback method. In chaining, return this object is mandate for Javascript method chaining. Using my solution you dont need to use then function
$provide.service("MyService", function () {
this.getMyData= function () {
var result = {
success: function (callback) {
callback({ ServerFileName: "myserverfilename"});
//returning main object for error callback invoke to occur
return this;
},
error: function (callback) {
callback({ ServerFileName: "myserverfilename" });
//returning this object will initialize error callback with object since you are chaining
return this;
}
};
return result;
}