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'
Related
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.
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.
This tells AngularJS that all values, factories and services defined inside the myUtilModule should be available inside the myOtherModule module too. In other words, myOtherModule depends on myUtilModule.
var myUtilModule = angular.module("myUtilModule", []);
myUtilModule.value ("myValue" , "12345");
var myOtherModule = angular.module("myOtherModule", ['myUtilModule']);
myOtherModule.controller("MyController", function($scope, myValue) {
});
My question is: What if you define controllers inside myUtilModule and try to use them on myOtherModule. Is this possible?
Yes, controllers will also be available between modules, a pratical example could be as follows:
angular.module('test.controllers').controller('HomeCtrl');
and then on your main module:
angular.module('test', [
'test.controllers',
'test.services',
...
];
This helps a lot on unit testing and reusability of packages.
I'm trying to define services dynamically in angularjs, as the docs says, $provide and $injector are services, thus they should be injectable in module.run..
I need to make the dynamic services available from app bootstrap, that's why i'm trying to define them in module.run
angular.module('remote.interface',[])
.run(['$provide', '$injector', function(provide, injector){
// provide dynamically
}]);
but this ends up in an Error : [$injector:unpr] Unknown provider: $provideProvider <- $provide, and same eror for $injector if i try to remove $provide injection.
Where's the bug?
[EDIT]
after some research i tried something like this:
var module = angular.module('remote.interface',[])
.run([function(){
var provide = module.provider(),
injector = angular.injector();
provide.value('my.val',{i:'am a value'});
injector.get('my.val'); // this throws [$injector:unpr] Unknown provider: my.valProvider <- my.val
}]);
even if i remove the injector.get call, if i try to inject my.val, for example, in another-module's controller, angular throws the same error.
Have a look at the documentation for module and have a read at the comments in the example setup, particularly these comments.
config
You can only inject Providers (not instances) into config blocks
run
You can only inject instances (not Providers) into run blocks
Here is an example setup on JSFiddle injecting $provide and $injector correctly.
https://docs.angularjs.org/guide/module
angular.module('myModule', []).
config(function(injectables) { // provider-injector
// This is an example of config block.
// You can have as many of these as you want.
// You can only inject Providers (not instances)
// into config blocks.
}).
run(function(injectables) { // instance-injector
// This is an example of a run block.
// You can have as many of these as you want.
// You can only inject instances (not Providers)
// into run blocks
});
I'm using AngularJS and trying to test a controller that calls a factory to get some data.
This is the controller code:
'use strict'
angular.module('AngularApp')
.controller 'IndexCtrl', ($scope, session, navigation) ->
session.find().then (response) ->
$scope.session = response.data
$scope.someOtherVariable = {}
Naturally, I'd like to swap the factory with a mock to prevent calling a real API. I'm trying to use $provide.factory to inject the mock copy:
'use strict'
describe 'Controller: IndexCtrl', ->
# load the controller's module
beforeEach module 'mosaicAdminWebClientApp'
beforeEach module ($provide) ->
$provide.factory 'session', ->
true
IndexCtrl = {}
scope = {}
# Initialize the controller and a mock scope
beforeEach inject ($controller, $rootScope) ->
scope = $rootScope.$new()
IndexCtrl = $controller 'IndexCtrl', {
$scope: scope
}
it 'should attach a list of awesomeThings to the scope', ->
expect(true).toBe true
When running this test with Karma, I guess this error:
Chrome 32.0.1700 (Mac OS X 10.9.1) Controller: IndexCtrl should attach a list of awesomeThings to the scope FAILED
Error: [ng:areq] Argument 'fn' is not a function, got Object
http://errors.angularjs.org/1.2.10-build.2176+sha.e020916/ng/areq?p0=fn&p1=not%20a%20function%2C%20got%20Object
at /Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:78:12
at assertArg (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:1363:11)
at assertArgFn (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:1373:3)
at annotate (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:3019:5)
at Object.invoke (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:3685:21)
at /Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:3554:71
at Array.forEach (native)
at forEach (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:303:11)
at Object.createInjector [as injector] (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:3554:3)
at workFn (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular-mocks/angular-mocks.js:2144:52)
Chrome 32.0.1700 (Mac OS X 10.9.1): Executed 10 of 10 (1 FAILED) (0.26 secs / 0.068 secs)
Warning: Task "karma:unit" failed. Use --force to continue.
Aborted due to warnings.
I've tried many different permutation, such as removing the label, passing an object or simple value instead of a function and none of them worked.
The documentation (http://docs.angularjs.org/api/AUTO.$provide) shows that I should call the factory() method with as factory(name, $getFn) where name is a string and $getFn is a function. That's what I'm doing but it's not working.
Anything I've missed? Does anyone know how to properly use $provide in Jasmine unit tests?
Thanks
Update:
I found a plunkr similar that solved an issue similar to this one here: stackoverflow.com/questions/19297258/why-is-provide-only-available-in-the-angular-mock-module-function-and-q-onl
I created my own plunkr with this code, but with the test code in JS instead of Coffee and got it to work: plnkr.co/edit/gfBzMXpKdJgPKnoyJy5A?p=preview
Now I manually converted the JS code into Coffee: plnkr.co/edit/qGGMayFjJoeYZyFKPjuR?p=preview
The error is back so the coffee code is wrong but the js code works.
Found the answer to my question.
Since CoffeeScript always returns the result from the last statement, in this case, CoffeeScript returns $provide inside of module(), which is wrong. An easy way to fix that is to just manually add a return statement after $provide, like this:
beforeEach module 'mosaicAdminWebClientApp', ($provide) ->
$provide.service 'session', ()->
return
Hope that will help someone else.