Mock an AngularJS module to pass to another - angularjs

I'm having some trouble mocking a factory belonging to one of my modules. The factory I would like to mock has 2 dependencies:
Factory class:
angular.module('serviceapp')
.factory('claims.service', ['applicationSettings', 'localStorageService', function (applicationSettings, localStorageService) {
//Factory code here
}]);
Test class:
//Instantiate some values
var mockAppSettings = {};
var mockStorageService = {};
var $factory; //Will hold my factory
//Targeting my module for mocking
beforeEach(angular.mock.module('serviceapp'));
//Providing some values for the dependencies of my module
beforeEach(module('serviceapp', function ($provide) {
$provide.value('applicationSettings', mockAppSettings);
$provide.value('localStorageService', mockStorageService);
}));
//Problems start here
beforeEach(inject(function ($injector) {
$factory = $injector.get('claims.service');
}));
I get an error message
Failed to instantiate module serviceapp due to:
Failed to instantiate module accountModule due to:
Module 'accountModule' is not available!
When investigating I see that accountModule is listed as a dependency for the serviceApp module.
App.module class:
angular.module('serviceapp', [accountModule])
However I'm having some trouble mocking this module to pass to serviceapp. I have tried to mock the accountModule in the same way I have mocked the serviceapp in the beginning however this is still bring up the same error message. How can I mock and pass one module to another?

angular.mock.module('serviceapp') shouldn't be read literally. It doesn't mock a module. It is the same thing as module('serviceapp') and is used in modular environments where module is reserved.
So, all that
beforeEach(angular.mock.module('serviceapp'));
beforeEach(module('serviceapp', ...));
does is loading serviceapp twice (doesn't hurt but doesn't help either).
To avoid Module 'accountModule' is not available!, it should be (re)defined:
beforeAll(() => {
angular.module('accountModule', [])
});
The problem with this approach is that even if it was defined, it will be overridden to the end of test run. If real accountModule needs to be used in other tests, this won't be possible.
The appropriate solution for similar design issues (this also applies to dependencies that aren't desirable in tests, e.g. router modules) is
angular.module('serviceapp', ['accountModule']);
angular.module('serviceapp.internal', [])
.factory('claims.service',...);
Here serviceapp serves as a shallow wrapper for serviceapp.internal, while the latter can be safely tested. If serviceapp is top-level module that is used for bootstrapping, this indicates that the application wasn't modularized enough, this hurts testing.

Related

Angular Controller can inject service just fine but $injector.get can't?

I have an extremely edge case scenario where I have a callback method I have to define during config. Which means no scope, no factories, etc... My work around is to use the root injector ($injector) and get my other modules at runtime.
However, when I call $injector.get('myServiceName') in my call back (after the application is running) I get "unknown provider". The same service has no problem (and actually is) being injected into a before my line of code is running. If I call $injector.get("myServiceNameProvider") then I can get the provider back in my callback.. But another service that only has a factory I can't get at all.
So in this extremely bad practice, how can I snag the service I configured. Heck I can't even seem to get $rootScope..
Angular inits providers first, then factories/services - services are not available in app.config.
You can deo like this:
app.config(...
window.on('scroll', function() {
// this is executed later
$injector.get()...
})
Or use app.run where services are available.
I think I had similar problem few months ago... I need to construct breadcrumbs, but they had to be created in config phase (ui-router). I have done it like this (breadcrumbs-generation-service.provider.js):
var somethingYouHaveToHaveLater = undefined;
function BreadcrumbsGenerationService () {
this.createStateData = createStateData;
function createStateData (arg) {
somethingYouHaveToHaveLater = arg;
};
}
function BreadcrumbsGenerationServiceProvider () {
this.$get = function BreadcrumbsGenerationServiceFactory () {
return new BreadcrumbsGenerationService();
}
}
angular
.module('ncAdminApp')
.provider('BreadcrumbsGenerationService', BreadcrumbsGenerationServiceProvider);
Because service is used inside Angular configs, needs to be injected as provider to be available in config phase: Similar SO Question. Despite the fact is registered as BreadcrumbsGenerationService needs to be injected as BreadcrumbsGenerationServiceProvider to config phase and used with $get():
BreadcrumbsGenerationServiceProvider.$get().createStateData(someParams);
But in controller, inject it without Provider suffix (BreadcrumbsGenerationServic) and it behaves as normal service.

Angular 1: $injector can't find a provider dependency when injecting provider inside a provider

My use case is: we have several helper classes, A and B, that are services, A depends on B, and I wanted to make them providers so that they can be used in .config phase.
I followed this SO answer to load a provider inside a provider.
As you can see here, it works:
http://plnkr.co/edit/SIvujHt7bprFumhxwJqD?p=preview
var coreModule = angular.module('CoreModule', []);
coreModule.provider('Car', function() {
//CarProvider.engine
this.engine = 'big engine';
//Car
this.$get = function() {
return {
color: 'red'
};
};
});
coreModule.provider('ParameterService', ['$injector', function($injector) {
try {
var CarProvider = $injector.get('CarProvider');
this.deepEngine = CarProvider.engine;
console.log('deepEngine = ' + this.deepEngine);
} catch (e) {
console.log("nope!")
}
// ParameterService
this.$get = function() {
return {};
};
}]);
coreModule.config(function(CarProvider) {
console.log('configEngine = ' + CarProvider.engine); // big engine
});
This works if I have Car and ParameterService in one file in this order.
However when I split Car and ParameterService into multiple files on disk, or I define ParameterService before Car in the same file, $injector.get('CarProvider') inside ParameterService fails.
How do I fix the issue?
I want to have one provider/service per file and I don't understand what is missing.
The order in which the services are defined doesn't matter during run phase, where service instances are injected. But it does matter during configuration phase, where service providers are injected, i.e. in provider constructors and config blocks.
Providers and config blocks are executed in the order in which they are defined. If Car provider is defined after ParameterService provider or config block, CarProvider doesn't exist at the moment when those two are executed.
To avoid potential race conditions, one module per file pattern should be followed. This allows to keep the app highly modular (also beneficial for testing) and never care about the order in which the files are loaded. E.g.:
angular.module('app', ['app.carService', 'app.parameterService']).config(...);
angular.module('app.carService', []).provider('Car', ...);
angular.module('app.parameterService', []).provider('ParameterService', ...);
Module parts are executed in the order in which the modules are defined in angular.module array hierarchy, from children to parents.
The decision if config block needs its own module depends on what it does (mostly for testing reasons).
It is possible to have providers in different files. You just need to attach them to the first module that you created.
If your markup looks like this:
<script src="coreModule.js"></script>
<script src="parameterService.js"></script>
Then, in coreModule.js, define your module:
angular.module('CoreModule', [])
.provider('Car', function() {
...
}
Remember, the second parameter ([]) tells angular to create a new module.
Then, declare your other provider in a different file, and attach it to your existing 'CoreModule' module:
angular.module('CoreModule')
.provider('ParameterService', ['$injector', function($injector) {
...
}
Notice that we are only passing one parameter to .module(). This tells angular to add your provider to an existing module.
Plunkr Demo

How can I obfuscate AngularJS codes?

How can I obfuscate AngularJS codes?
We have tried gulp-obfuscate, but it does not work. Anyone can help us? Thanks a lot!
We have done like this
someModule.controller('MyController',[ '$scope', function($scope) {",
but after success to get the obfuscated codes like this
function H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅4() {
var H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅1, H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅2, H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅3;
...
H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅3 = H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅1 + H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅2;
return H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅3;
}
all angularjs codes in app-52143d391a.js not worked, it return
Uncaught Error: [$injector:nomod] Module 'client' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
After run
gulp.task('obfuscate', function () {
return gulp.src('../webapp/scripts/app-*.js')
.pipe(ngAnnotate())
.pipe(obfuscate())
.pipe(gulp.dest('dist'));
});
all are ok, and get below codes:
http://pan.baidu.com/s/1ntXuGbB
then run with below error:
Uncaught TypeError:  _ 206. _ 252 is not a function(anonymous function) # app-6c38d9a2fc.js:2
generated.js:9279Uncaught Error: [$injector:modulerr] Failed to instantiate module client due to:
Error: [$injector:nomod] Module 'client' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.4.7/$injector/nomod?p0=client
From the ngDocs:
Careful: If you plan to minify your code, your service names will get
renamed and break your app.
To make your code work after minification you have to use the inject property, every-time you use dependancy injection, to annotate your components. So even if the variable names change angular will know how to map the mangled variables with the proper service.
e.g:
Here we pass the $scope with dependency injection:
someModule.controller('MyController', function($scope) {
For this line to work you have to change to this:
someModule.controller('MyController',[ '$scope', function($scope) {
Or:
MyController.$inject = ['$scope'];
someModule.controller('MyController', function($scope) {
Or even better.
Use gulp-ng-annotate before the obsfucate task which does all that work for you.
UPDATE
After Obsfucation you should see the $inject string as they where, instead this plugin obsufucates them also. Meaning:
['$http', function($http) {
becomes:
[ "ಠ_ಠ727", function( ಠ_ಠ727 )
instead of:
[ "$http", function( ಠ_ಠ727 )
There is a relevant issue on github
I would suggest you use another plugin. With gulp-uglify I didn't have any issue.

AngularJS two different $injectors

Today I found, that $injector injected to config or provider is different from $injector injected to service, factory or controller.
And get() function from this $injectors works differently.
$injector from config or provider, can't get() any service! $injector.get('myService') throws Error: [$injector:unpr] Unknown provider: myService, but $injector.has('myService') return true. That's very very strange.
$injector from service or controller works normally.
Here is a code sample for better understanding:
angular.module('app', [])
.provider('myProvider', function ($injector) {
this.$get = ['$injector', function (serviceInjector) {
return {
providerInjector: $injector,
serviceInjector: serviceInjector
};
}];
})
.service('myService', function () {})
.controller('myCtrl', function ($scope, myProvider) {
var providerInjector = myProvider.providerInjector;
var serviceInjector = myProvider.serviceInjector;
console.log(providerInjector === serviceInjector); // -> false
console.log(serviceInjector.has('myService')); // `serviceInjector` has `myService`
console.log(getMyService(serviceInjector)); // `serviceInjector` can get `myService`
console.log(providerInjector.has('myService')); // `providerInjector` has `myService` too!
console.log(getMyService(providerInjector)); // but `providerInjector` can't get `myService`! =(
function getMyService(injector) {
try {
injector.get('myService');
return "OK";
} catch (e) {
return e.toString();
}
}
});
Here is a plunker to play
Can anybody explain why there is two different injectors?
And how can I use $injector from provider/config to inject service(after service was initialized, of course)?
P.S. I use angular 1.3.13
I found this issue on github: https://github.com/angular/angular.js/issues/5559
In the config function, $injector is the provider injector, where in the run function, $injector is the instance injector.
One's the $injector at the config stage (only providers and constants accessible), and one's the $injector at the run stage. The confusion may be that you're thinking the $injector modifies itself to include the new stuff as it crosses the line from config to run, but that's not true. They're two separate (although related) objects, with their own caches of instances.
A more in-depth reason for this dichotomy will probably come from a deep learning of the $injector internals, but it seems like it's been DRY-ed pretty hardcore, and the two types of injectors share almost all the same behavior, except in how they deal with "cache misses" in their instance caches.
We are going to overhaul the injector in v2, so this will get fixed there (getting rid of the config phase is one of the objectives of the injector v2).
Seems like there is really two different injectors, and angular developers will not fix that behavior(in versions <2.0). And nobody added a note about that aspect to $injector docs for some reason.
I was unable to find a way how to really get instance injector inside a configuration block without hacky tricks. So, I write a cute provider to solve that kind of problems.
.provider('instanceInjector', function () {
var instanceInjector;
function get() {
return instanceInjector;
}
function exists() {
return !!instanceInjector;
}
angular.extend(this, {
get: get,
exists: exists
});
this.$get = function ($injector) {
instanceInjector = $injector;
return {
get: get,
exists: exists
};
}
})
// We need to inject service somewhere.
// Otherwise $get function will be never executed
.run(['instanceInjector', function(instanceInjector){}])
Ok. After reading your comments, here is my answer.
I edited code in plunk to make it work, when invoking the providerInjector.get() the code should be as follows:
$scope.getMyServiceFromProviderInjector = function () {
try {
myProvider.providerInjector.get('myServiceProvider');//here is change in provider name
return "OK";
} catch (e) {
return e.toString();
}
};
According to angular docs the following is quoted for config and run blocks:
Configuration blocks - get executed during the provider registrations and configuration phase. Only providers and constants
can be injected into configuration blocks. This is to prevent
accidental instantiation of services before they have been fully
configured.
Run blocks - get executed after the injector is created and are used to kickstart the application. Only instances and constants can be
injected into run blocks. This is to prevent further system
configuration during application run time.
This simply means, you cannot get instances of services inside config blocks.
I wrote this a while back, which explains the lifecycle of both injectors of AngularJS, i.e providerInjector and instanceInjector.
http://agiliq.com/blog/2017/04/angularjs-injectors-internals/

How to mock Angular modules before injecting?

I have the following dependencies in my application:
angular.module 'MyApplication.NetworkingModule', ['ngResource']
.value 'NetworkingValues'
angular.module 'MyApplication.MyModule', []
.factory 'MySpecificResource', ['NetworkingValues', ...]
.factory 'MyService', ['MySpecificResource', ...]
So MySpecificResource depends on NetworkingValues.
The thing is I'd like to mock some of these values before using them in resources in tests.
Doing this:
beforeEach module 'MyApplication.NetworkingModule'
beforeEach module 'MyApplication.MyModule'
causes that factory is invoked immediately and I don't have a chance to replace some of NetworkingValues.
beforeEach inject (#MyService, #NetworkingValues) ->
MyService depends on MySpecificResource, which depends on NetworkingValues that I want to stub before tests.
What are the correct way to do that?
Have you tried something along these lines?
var mockedValues;
beforeEach(module(function($provide) {
$provide.value('NetworkingValues',mockedValues);
}));
beforeEach(inject(function(_NetworkingValues_) {
mockedValues = {....};
}));
The $provide will supply your mockedValues instead of the actual NetworkingValues.
So my final solution was to provide constant and inject it together with module function:
beforeEach module "MyApplication.NetworkingModule", ($provide) ->
$provide.constant 'NetworkingValues', endpointGet: 'my-endpoint/:id'

Resources