Angular unit-test - mocking method in controller - angularjs

I need to mock the method of service in the controller. I know how to mock simple service like service.method, but not like this one. I dont know how to mock "action.user.update". If i tried to spy on it, i received an error 'Cannot read property 'update' of undefined'.
JsFidleDemo
My service:
.service('action', ['$http', '$q', function ($http, $q) {
var service = {};
service.user = {
update: function (data, config) {
return service.run({
name: config.name,
call: $http({
method: "POST",
url: "/user/edit",
data: data
}),
success: config.success,
error: config.error
});
}
};
return service;
}]);

You got half way there
$provide.value('action', action);
where action should be an object you create in unit tests
i.e.
action = {
user: {
update: jasmine.createSpy('action.user.update')
}
}
$provide.value('action', action);
and then in test
scope.saveUser()
expect(action.user.update).toHaveBeenCalled()
updated fiddle
http://jsfiddle.net/3oavnmev/1/

Related

Angular Js:How to pull factory data to the controller

Hi I am trying to pull my angular js factory data to my controller,
please have a look if there is any issue.
factory.js
.factory('History', ['$http', '$q', function ($http, $q) {
function history () {
// angular.extend(SearchOptions.getDefaults(), params, options);
var deferred = $q.defer();
$http({
method: 'GET',
url: '/res/orders/' + 31536427 + '/history-group'
})
.success(function (res) {
// console.log(res);
})
.error(function (err) {
// TODO error handler
deferred.reject(err);
});
return deferred.promise;
}
return {
history: history
};
}]);
controller.js
.controller('HistoryCtrl', ['$scope', '$state', '$stateParams', 'History', function($scope, $state, $stateParams, History) {
History.history().then(function(res) {
console.log(res);
$scope.history = res.body;
console.log($scope.history);
}, function(err) {
// TODO error handler
console.log(err);
})
.finally(function(err) {
});
}]);
You need to pass the response in the success function in the 'History' factory as below:
.success(function (res) {
// console.log(res);
deferred.resolve(res);
})
The issue with your code is you are not resolving the promise after getting the data in the success callback function. Resolve it as shown below in the .success callback function :
deferred.resolve(res);
Few points to improve your code:
$http service in Angular by default returns a promise. Hence, you don't have to explicitly construct a promise using $q which is an anti pattern (Deferred anti-pattern). Just returning $http object from the service itself will do the
job. Doing return $http() is equivalent to return deferred.promise() in your code.
.success and .error callbacks are deprecated in the latest version(1.6) of AngularJs (Deprecation Notice). The disadvantage of using these is that they are not chainable as they ignore return values. Hence, it is better to use .then instead.
Applying above changes, your service can be refactored to below :
.factory('History', ['$http', function ($http) {
function history () {
return $http({
method: 'GET',
url: '/res/orders/' + 31536427 + '/history-group'
})
.then(successCallback, errorCallback);
}
function successCalback (res) {
return res;
}
function errorCalback (err) {
return err;
}
return {
history: history
};
}]);

MVC and Angularjs : promise does not waiting data

i'm newby in angularjs i researched on the internet but i couldn't find any suitable solution for my problem. I made an http call to get some data from controller. The controller side is okay. But the client-side, promise does not waiting data. Here codes that I wrote ;
//service code
angular.module("myApp").service('$myService', function ($http, $q) {
this.getDataArray = function () {
var deferred = $q.defer();
$http.get('../Home/GetDataArray')
.success(function success(response) {
deferred.resolve(response);
})
.error(function () {
console.log("error getting data array");
deferred.reject();
});
return deferred.promise;
};
}
// controller-code
angular.module("myApp").controller('dataController', function ($scope, $http, $myService) {
$scope.getDataFromService = function () {
$myService.getDataArray().then(function (response) {
$scope.dataArray = response.data;
});
};
});
}
When i call the getDataFromService method at first $scope.dataArray is empty, but the second call, $scope.dataArray is filled with data. Where is the problem? Thanks for helps.
Not an angular expert myself. This is just how I did it when I ran into the same problem. Try this:
Controller:
angular.module("myApp").controller('dataController',[ '$scope', 'Service1', '$http', function ($scope, Service1, $http) {
var deferred = Service1.getDataArray().$promise;
return deferred.then(function successCallback(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
$scope.dataArray = response.data;
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
})
}])
and service:
var service = angular.module("myApp").service('myService', ['ngResource']);
myService.factory('Service1', ['$resource',
function ($resource) {
return $resource('../Home/GetDataArray', {}, {
get: { method: 'GET', isArray: true },
});
}])
The idea is that your service isn't the one that should wait for a return, your controller is. So you should wait for the promise in your controller not your service. In my example I am using factories because, well, that's how I got around it in my project, you can try and implement this directly if you don't want to use a factory.

How can I extend the $http service in angular?

Unfortunately, we're stuck running 1.2.26 (will upgrade to 1.2.28 when it's gemified).
In the meantime, how can I patch (heh) $http so that the short-hand patch method is available? I'm pretty new to the whole service/factory/module thing. I've done hours of searching and can't seem to figure it out.
myApp.factory('patchedHTTP', function($http, BasicService) {
// $http['patch'] = function(url, data, config) {
// return $http(angular.extend(config || {}, {
// method: 'patch',
// url: url,
// data: data
// }));
// };
var extended = angular.extend(BasicService, {});
extended.createShortMethodsWithData('patch');
return extended;
});
Above is the best I've got... and it doesn't do anything XD
You can do this with an angular decorator.
A service decorator intercepts the creation of a service, allowing it to override or modify the behaviour of the service. The object returned by the decorator may be the original service, or a new service object which replaces or wraps and delegates to the original service.
For more information you can check angular documentation.
Example:
var app = angular.module('app');
app.decorator('$http', function ($delegate) {
// NOTE: $delegate is the original service
$delegate.patch = function () {
// do the implementation here
};
return $delegate;
});
// usage
app.controller('SomeController', function($http) {
$http.patch();
});
You can keep this decorator until you upgrade to some newer version and than just safely delete it.
The module.decorator has been added to the module API in version 1.4. That's why it is not working in 1.2.x.
Please find below a working demo or here at jsfiddle.
It took me a while to implement the patch method because I've missed to return the promise of $http. But now it should be working.
angular.module('patchDemo', [])
.config(function ($provide) {
$provide.decorator('$http', function ($delegate) {
// NOTE: $delegate is the original service
$delegate.patch = function(url, data, config) {
var paramsObj = angular.extend({}, config || {}, {
method: 'PATCH',
url: url,
data: data
});
return $delegate(paramsObj);
}
return $delegate;
});
})
.controller('MainController', MainController);
function MainController($http) {
console.log($http.patch);
//$http({method: 'PATCH', url: 'http://jsonplaceholder.typicode.com/posts/1', data: {title:'foo'}}); //>>>>>working long version of patch
$http.patch('http://jsonplaceholder.typicode.com/posts/1', {
title: 'foo'
}).then(function(response) {
console.log(response);
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.26/angular.js"></script>
<div ng-app="patchDemo" ng-controller="MainController"></div>

How to call factory/service from controller in AngularJS? error: Unknown Provider error in component $injector

I'm developing a mobile app with AngularJS. First I called all $http requests inside a controller as I would usually do. Now that I've read more about Angular I've seen that that may not be the best practice, especially if you want to share data between controllers. So i'm now reinventing my controller/service system.
Now to the problem. I'm calling a service from the controller, but I'm getting an error: Unknown Provider error in component $injector
Could anyone help me?
The codez:
listdnModule.factory('getActiveDnService', function ($scope, $http) {
return {
getActiveDnSvc: function (id) {
return $http({
method: 'POST', url: 'svc.aspx/getActiveDN', data: "{'id':" + id + "}", cache: true
});
}
};
});
listdnModule.controller('listdnCtrl', ['$scope', '$http', 'getActiveDnService', function ($scope, $http, svc) {
$scope.mjav = 1;
svc.getActiveDnSvc($scope.mjav).success(function (data) {
$scope.listdn = data.d;
});
}]);
Remove $scope from the injected dependencies of the service, $scope can only be injected to controllers, and passed in as parameters of link functions of directives in angular. Your service shoule look like:
listdnModule.factory('getActiveDnService', function ($http) {
return {
getActiveDnSvc: function (id) {
return $http({
method: 'POST', url: 'svc.aspx/getActiveDN', data: "{'id':" + id, cache: true
});
}
};
});

Is there a way I can return a promise from a $resource without needing to use $q?

I have coded the following service:
angular.module('App')
.factory('TestService', ['$http', '$q', '$resource', function (
$http, $q, $resource) {
var TestResource = $resource('/api/Tests', {}, {
saveData: { method: 'PUT' },
deleteData: { method: "DELETE", params: { TestId: 0 } }
});
var factory = {
query: function (selectedSubject) {
var deferred = $q.defer();
TestResource.query({ subjectId: selectedSubject },
function (resp) {
deferred.resolve(resp);
}
);
return deferred.promise;
//return Test.query({ subjectId: selectedSubject });
}
}
return factory;
}]);
In my controller I am calling it this way:
TestService.query($scope.selectedSubject)
.then(function (result) {
$scope.gridData = result;
}, function (result) {
alert("Error: No data returned");
});
Is there a way that I could cut out a few lines of code by not having $q in my service. Is there a way that I could return a promise from the $resource? I have tried the commented out code but that gives me an error saying there is no ".then" method.
$resource can't return a promise in the current stable version of Angular (1.0.8), but it looks like it'll come in version 1.2. It was added in v1.1.3 (v 1.1 is the unstable branch):
$resource: expose promise based api via $then and $resolved (dba6bc73)

Resources