How to test an AngularJS provider configuration? - javascript

I have a provider defined as follows:
(function(angular) {
angular.module('myModule', [])
.provider('myService', function () {
var service = {};
service.configureSomething = function () { };
service.$get = function () { return {}; };
return service;
});
})(angular);
How can I test configureSomething()?
The example shown in the AngularJS documentation assumes the provider is a public function rather than being an anonymous function passed inside .provider() within a (function(){})() approach.
Please note that I don't want to test for the provider instance, I'm just interested in testing the configuration.

Here's the answer provided by Mark Gemmill based on Eitan Peer's answer to my same question posted in the AngularJS Google Groups.

Related

angularjs how to pass parameters to named function from controller

Inside the controller I am trying to breakup my code into named functions for readability. However, in the parameterized named functions the scope and the injected dependency are all null. How do access these inside the named functions. Thanks for you help.
(
function() {
'use strict';
var moduleName = 'ufsrAppModule';
var controllerName = 'ufsrController';
var dependencyInjection = ['api', 'appHost', 'userAccount', 'userProfileFactory', 'fsrFactory', 'userFsrFactory', internalFunc];
angular.module(moduleName)
.controller(controllerName, dependencyInjection);
function internalFunc(api, appHost, userAccount, userProfileFactory, fsrFactory, userFsrFactory) {
var vm = this; //controller AS in ng-controller, do not use $scope
init(api, appHost, userAccount, userProfileFactory, fsrFactory, userFsrFactory, vm);
}
function init(api, appHost, userAccount, userProfileFactory, fsrFactory, userFsrFactory, vm) {
vm.facilityChanged = facilityChanged;
...
...
function facilityChanged(vm, fsrFactory) {
/*update UI then retrieve services*/
vm.postStatus = undefined;
vm.services = undefined;
vm.roles = undefined;
vm.services = fsrFactory.service().query({
/*parameters*/
FacilityID: vm.facility
})
.$promise.then(
function(data) {
vm.services = data;
});
}
}
})();
Strict DI can be done separately in this style
angular.module(moduleName)
.controller(controllerName, controllerFunction);
controllerFunction.$inject = ['$scope', '$http'];
function controllerFunction($scope, $http) {
...
}
This style is also recommended by John Papa's Angular style guide.
The facilityChanged is not working because its parameters are overwriting those that are passed into init
It can be fixed by changing
function facilityChanged(vm, fsrFactory) {
to
function facilityChanged() {
Edit: Attached jsbin
I strongly recommend putting the init function inside your controller function to save the parameter passing, just like the activate function in John Papa's guide.
Refined jsbin

Do not understand the controllers in Angular JS

This is a question I am not used to ask, but I feel like this is the only way to understand what I am struggling with. As shown below, there are two functions app.controller() and app.factory(), to me both of them seems to be equal even though if I delete one part the function does not perform its intended task.
Link to plunker: click here (Relevant file is script.js)
What is the difference between these two functions, why cant I have only one of them? I know there must be a simple explanation.
app.controller('MainCtrl', ['$scope', 'ItemsService', function ($scope, ItemsService) {
$scope.newItem = { PlateNumber: '', Description: '', Price: 0 };
$scope.currentItem = null;
$scope.items = ItemsService.getItems();
$scope.addItem = function () {
ItemsService.addItem(angular.copy($scope.newItem));
$scope.newItem = { PlateNumber: '', Description: '', Price: 0 };
};
$scope.updateItem = function (id) {
ItemsService.updateItem(id);
};
$scope.removeItem = function (id) {
ItemsService.removeItem(id);
};
}]);
vs
app.factory('ItemsService', ['$firebase', 'FIREBASE_URI', function ($firebase, FIREBASE_URI) {
var ref = new Firebase(FIREBASE_URI);
var items = $firebase(ref);
var getItems = function () {
return items;
};
var addItem = function (item) {
items.$add(item);
};
var updateItem = function (id) {
items.$save(id);
};
var removeItem = function (id) {
items.$remove(id);
};
return {
getItems: getItems,
addItem: addItem,
updateItem: updateItem,
removeItem: removeItem
}
}]);
Controllers are only instantiated when you need one, (you use ng-controller or controllerAs). So every time you switch to a different route or page, Angular instantiates a controller (it can be the same one that's cleaned up, for example if you refresh the page.)
Angular providers, of which there are a few main kinds - Factory being one of them, are a way to keep data around for the lifetime of the app or even used to pass data between different controllers. The scope of your question is more like: Provider vs Controller, not Factory(which is one type) vs Controller.
In your example above, you have ONE controller, what if you have many? How can you pass data or utility functions to many controllers without writing the same code over and over again? With Providers for one!
Here are some good links for you to check out:
Angular Provider Docs
Blog post by Tyler McGinnis explaining the above further
JSFiddle with Example of a Factory vs a Service

how should be used a service "factory" in a provider of angularjs?

I'm trying to use a service "factory" in service "provider".
How is the best way to do it.
I'm working with different modules. but it is generates a mistake me when I import the service to the new function.
What is the best way to reuse or inject services in a service "provider".
http://jsfiddle.net/aurigadl/facgT/5/
var moduleService = angular.module('templatesServices', []);
moduleService.factory('templateSrv',['$q', function($q){
return {
data: function(){
console.log('In factory');
return 2000;
}
}
}])
function login(templateSrv){
this.$get = angular.noop;
this.getAcces = function(){
return 'some data';
};
}
login.$inject = ['templatesServices'];
angular.module('loginProvider', ['templatesServices']).provider('login', ['caliopewebTemplateSrv',login]);
Providers are available in a phase before any solid services or other components are ready, so you won't be able to inject your service directly into the provider.
You can, however, inject services into the $get function of your provider, used when you actually instantiate a service. So your loginProvider cannot access templateSrv but your login service can do, so long as the module in which templateSrv is defined 'requires' that in which loginProvider is defined. It appears to be doing this in your example (angular.module('loginProvider', ['templatesServices'])) but the fiddle seems very different.
var templateModule = angular.module('templateModule', []);
templateModule.factory('templateSrv',[function(){
return {
// ...
};
}]);
function loginProvider(){
this.$get = ['templateSrv', 'otherService', function(templateSrv, otherService) {
// Use templateSrv/otherService (from templateModule or loginModule) somehow
}];
this.getAccess = function(){
return 'some data';
};
}
angular.module('loginModule', ['templateModule'])
.provider('login', loginProvider);
I don't quite follow your intentions so I can't give much more information, but I hope this helps.
Sources:
http://docs.angularjs.org/guide/module
http://docs.angularjs.org/api/AUTO.$provide

Using spies in non testing code in angularjs

I recently dug a little deeper into unit testing. I was wondering if there is a way to use spies in production code as well. I've a tracking service. It would be nice to access other services and maybe even controllers, without haveing to alter their code.
Is there a way to spy on methods being called from services and controllers in the application code and what would be the best way to do so?
EDIT
Atm. I'm using this pattern for spying on services:
var vSetFNTrigger = function (sEvent, fnTrigger) {
fnTrigger.obj[fnTrigger.sMethod] = (function () {
var fnCached = fnTrigger.obj[fnTrigger.sMethod];
return function () {
$rootScope.$broadcast(sEvent, {});
return fnCached.apply(this, arguments);
};
})();
};
fnTrigger: {
obj: formData, // the service
sMethod: 'qPost' // the method to spy on
},
EDIT 2
I forgot to add a return to the inner function.
There should be nothing stopping you from doing this, although I think it is the wrong tool for the job.
If you are in Angular, you should consider using a decorator pattern. You can even use the provider decorator to intercept pretty much anything in Angular.
For instance, you might have a spy function that looks like this:
function createSpy(serviceName, source, spyNames, rootScope) {
var spy = angular.extend(angular.isFunction(source) ? function () {
console.log("Called " + serviceName + '()', arguments);
// broadcast with rootScope
return source.apply(source, arguments);
} : {}, source);
spyNames.forEach(function(name) {
var original = spy[name];
spy[name] = function() {
console.log("Called " + serviceName + '.' + name, arguments);
// broadcast with rootScope
return original.apply(spy, arguments);
};
});
return spy;
}
Then, you can create a generic function to generate a decorator:
function decorateWithSpy($provide, service, spyNames) {
$provide.decorator(service, function($delegate, $rootScope) {
return createSpy(service, $delegate, spyNames, $rootScope);
});
}
You can configure your spies like this:
app.config(function($provide) {
decorateWithSpy($provide, '$http', ['get']);
decorateWithSpy($provide, '$compile', []);
});
Doing this causes all of my $http and $compile functions to get printed to the console.

Call angularjs service from simple js code

I have the following angularjs service:
angular.module('app.main').factory('MyService', ["$http", function ($http) {
return new function () {
this.GetName = function () {
return "MyName";
};
};
}]);
How can I call GetName function from MyService from legacy js code?
Use angular.injector. Using your code you can do something like the following:
angular.module('main.app', []).factory('MyService', ['$http', function ($http) {
return new function () {
this.GetName = function () {
return "MyName";
};
};
}]);
angular.injector(['ng', 'main.app']).get("MyService").GetName();
Here is the jsfiddle: http://jsfiddle.net/wGeNG/
NOTE - You need to add "ng" as your first module before loading your custom module since your example code depends upon $http provider which is in the ng module.
EDIT - Using get() as in OP's answer but note this code is fetching the service without relying upon the element being bound to the app module "main.app".
For me it worked with:
angular.element(document.body).injector().get("MyService")
I got 'Unknown provider' error when tried this:
angular.injector(['ng', 'main.app']).get("MyService")
and as i am using jqLite, i can't do
angular.element('*[ng-app]')
because selectors are not supported by jqLite, but i got [Dor Cohen] idea. My directive ng-app is on my body tag, then i can use:
angular.element(document.body).injector().get("MyService")
or
angular.element(document).find('body').injector().get("MyService")
Using the following line helps to execute my method from the angularjs service:
angular.element('*[ng-app]').injector().get("MyService").GetName ();
Here's a utility method that I use :
function getService(name, element) {
element = element || '*[ng-app]';
return angular.element(element).injector().get(name);
}
getSrv("name-of_service", document.body)

Categories