I am testing some Angular.js code with Jasmine. To do this I need an Angular injector:
var injector = angular.injector(['ng', 'ngRoute', 'mainModule']);
This works fine, but when I get to testing a controller that uses $location, I have to add that to the injector:
var injector = angular.injector(['ng', 'ngRoute', 'location', 'mainModule']);
Now this throws an error:
Error: Error: [$injector:modulerr] Failed to instantiate module location due to:
Error: [$injector:nomod] Module 'location' 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.
How can I include $location service there? Thanks.
-------------- EDIT -----------------
due to not many answers I thought I will add some details.
Here is a controller I am trying to test (or the definition of it, I won't be pasting all code):
var app = angular.module('mainModule');
app.controller('appController', ['$window', '$scope', '$location', '$wsService', '$userService', '$labelService', function ($window, $scope, $location, $wsService, $userService, $labelService) {
//body of the controller
}]);
You have already seen how I create the injector in my unit test in the original post, here is how I use the injector to create the controller:
var scope;
var controller;
var getController = function() {
injector.invoke( ['$rootScope', '$controller', function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('appController', {$scope: scope});
}]);
};
Hope this will help with producing some potential answers. Thanks
To answer my own question. Here is how to test the following controller:
var app = angular.module('mainModule');
app.controller('appController', ['$window', '$scope', '$location', '$userService', function ($window, $scope, $location, $userService) {
//body of the controller
}]);
1 In unit test create injector:
var injector = angular.injector(['ng', 'ngRoute', 'mainModule']);
2 Invoke services required:
injector.invoke( ['$userService', function ($userService) {
service = $userService;
}]);
3 Mock location service:
var mockLocation = {
path : ""
};
I just needed path for the controller I am testing, thus I did not mock anything else, but mock whatever else you need.
4 Invoke the controller:
var scope;
var controller;
var getController = function() {
injector.invoke( ['$rootScope', '$controller', function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('appController', {$scope: scope, $location: mockLocation});
}]);
};
Now the controller can be used in unit tests.
This did get rid of 'unknown provider' and other errors related to location service.
This blog post helped me with an answer, thanks to the author.
Related
I've included 3 files in my karma config : 1. angular.js, angular-mock.js, and login.spec.js
this is my login.spec.js:
describe("Hello World example", function() {
beforeEach(module("app"));
var loginCtrl,
scope;
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
loginCtrl = $controller('loginCtrl', {
$scope: scope
});
}));
it('says hello world!', function () {
expect(scope.hello).toEqual("Hello");
});
});
so my login.js (controller) look like this
angular.module('app')
.controller('loginCtrl', function($scope, $rootScope, $location) {
$scope.hello = 'hello';
});
but I got Module 'app' is not available!
I don't see you creating the app module anywere. You should include the app.js and all the sources your tests need to your karma.json as well.
You just need to add this line to the top of your login.js file:
This declares the module:
angular.module('app', []);
This gets an instance of the module(as you are doing):
angular.module('app')
.controller('loginCtrl', function($scope, $rootScope, $location) {
$scope.hello = 'hello';
});
Here is a working Plunkr that is very similar:
http://embed.plnkr.co/O1otV47VxsIL5krAdUW0/preview
You also mentioned that you had added 3 files to the karma config and didn't mention the login.js. That will also need to be added.
I'm trying to separate components into several files for a simple application but angular's dependency injector is giving me headaches and I don't really know what is expected.
Unknown provider: servicesProvider <- services <- maincontroller
Is the error I'm getting.
app.js
//Application definition with injected dependencies
var app = angular.module('leadcapacity', ['services', 'utils', 'customfilters', 'controllers']);
services.js
var services = angular.module('services', []);
services.service('xrmservice',
[
'$http', function($http) {
var oDataUrl = Xrm.Page.context.getClientUrl() + '/XRMServices/2011/OrganizationData.svc/';
var service = {};
service.query = function(entitySet, query) {
return $http.get(oDataUrl + entitySet + '?' + query);
};
return service;
}
]);
controllers.js
var ctrls = angular.module('controllers', ['utils', 'services']);
ctrls.controller('maincontroller',
function ($scope, services, utils) {
};
});
And the include order in index.html
<script src="service.js"></script>
<script src="controllers.js"></script>
<script src="app.js"></script>
Looks fine to me. I know this is perhaps not the best way to organize things, but getting a "Hello world" first would be nice.
Thanks.
Error message appearing in console clearly says that, services
dependency isn't exists in the module.
You have injected incorrect service name in maincontroller controller factory function, basically you were trying to to inject services(module name) instead of xrmservice(service name)
function ($scope, services, utils) {
should be
function ($scope, xrmservice, utils) {
Additional
Do follow Inline Array annotation of DI, as you were already used the same in your xrmservice service JS file, so that in future you don't need to go back and change that when you face javascript minification related issues.
Controller
ctrls.controller('maincontroller', [ '$scope', 'xrmservice', 'utils',
function ($scope, xrmservice, utils) {
//code goes here
//....
};
}]);
Although you have injected them into the module, you need to give them to the function so you can use the injected modules
ctrls.controller('maincontroller',
['$scope', 'services', 'utils', function ($scope, services, utils) {
};
}]);
In my Angular app
var mainApp = angular.module('mainApp', ['ngCookies']);
I've defined authCtrl controller:
mainApp.controller('authCtrl', ['$scope, $cookies',function ($scope, $http, $cookies) {
$scope.credentials = {};
$scope.signCheck = function () {
a = $cookies.getObject('session_credentials');
console.log(a);
};
}]);
If I'm removing $scope declaration from array (injection array?)
mainApp.controller('authCtrl', ['$cookies',function ($scope, $http, $cookies) {
$scope becomes undefined.
If I'm removing $cookies — $cookies becomes undefined.
If I keep them both — $injector unknown provider error.
What I'm doing wrong?
Just be sure that you indicate the services in a correct order in the injector array and the controller function params:
Angular docs says:
This is the preferred way to annotate application components. This is
how the examples in the documentation are written.
For example:
someModule.controller('MyController', ['$scope', 'greeter', function($scope, greeter) {
// ...
}]);
Here we pass an array whose elements consist of a list of strings (the
names of the dependencies) followed by the function itself.
When using this type of annotation, take care to keep the annotation
array in sync with the parameters in the function declaration.
Perhaps this controller definition will work for you:
mainApp.controller('authCtrl', ['$scope', '$http', '$cookies', function ($scope, $http, $cookies) {
$scope.credentials = {};
$scope.signCheck = function () {
a = $cookies.getObject('session_credentials');
console.log(a);
};
}]);
In all the angularjs tutorials I am gone through. Modules are created as follows
var newApp = angular.module('articles', []);
or
var routerApp = angular.module('routerApp', ['ui.router']);
I started my project with meanjs boiler plate code and controller starts as follows
angular.module('articles').controller('ArticlesController', ['$scope', '$stateParams', '$location', 'Authentication', 'Articles',
function($scope, $stateParams, $location, Authentication, Articles) {
$scope.authentication = Authentication;
.....
.....
]);
When I change it to
var newApp = angular.module('articles',[]);
newApp.controller('ArticlesController', ['$scope', '$stateParams', '$location', 'Authentication', 'Articles',
function($scope, $stateParams, $location, Authentication, Articles) {
$scope.authentication = Authentication;
.....
.....
]);
All the routes of articles stops working. If I want to include a new component into the module, how do I do it. I want to add angularFileUpload in to my module.
Sample code in angularfileupload is
angular
.module('app', ['angularFileUpload'])
.controller('AppController', function($scope, FileUploader) {
$scope.uploader = new FileUploader();
});
How do I add ['angularFileUpload'] if the module is already registered?
Edit:
articles.client.modules.js
'use strict';
// Use Applicaion configuration module to register a new module
ApplicationConfiguration.registerModule('articles');
angular.module("MyModule", []) (with []) is a setter function, that is - it registers a module.
angular.module("MyModule") without [] is a getter function, it retrieves a previously registered module.
Calling a setter twice re-defines the module.
I'm not familiar with meanjs boilerplate, but in all likelihood when you used a setter, you have redefined the module and whatever controllers, services, config, etc... that were previously registered were overwritten.
All you need to do is change what you added to:
var newApp = angular.module("articles");
Example:
angular.module("M", []).controller("A", function(){}); // module M has controller A
angular.module("M").controller("B", function(){}); // module M has controllers A, B
var app = angular.module("M", []); // module M re-registered
app.controller("C", function(){}); // module M has controller C only
// Create a new module
var myModule = angular.module('myModule', []);
// register a new service
myModule.value('appName', 'MyCoolApp');
// configure existing services inside initialization blocks.
myModule.config(['$locationProvider', function($locationProvider) {
// Configure existing providers
$locationProvider.hashPrefix('!');
}]);
Usage
angular.module(name, [requires], [configFn]);
I'm trying to test my application with Karma but I get following Errors:
minErr/<#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:78:5
loadModules/<#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:3859:1
forEach#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:325:7
loadModules#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:3824:5
createInjector#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:3764:3
workFn#/home/usr/webui-ng/src/client/app/bower_components/angular-mocks/angular-mocks.js:2150:9
These are my files:
hello.js
angular.module('myApp', [])
.controller('MainController', function($scope) {
$scope.name = "Ari";
$scope.sayHello = function() {
$scope.greeting = "Hello " + $scope.name;
}
})
hello.js - test file :
describe('Unit: MainController', function() {
// Load the module with MainController
beforeEach(module('myApp'));
var ctrl, scope;
// inject the $controller and $rootScope services
// in the beforeEach block
beforeEach(inject(function($controller, $rootScope) {
// Create a new scope that's a child of the $rootScope
scope = $rootScope.$new();
// Create the controller
ctrl = $controller('MainController', {
$scope: scope
});
}));
it('should create $scope.greeting when calling sayHello',
function() {
expect(scope.greeting).toBeUndefined();
scope.sayHello();
expect(scope.greeting).toEqual("Hello Ari");
});
});
As you can see, I've already loaded the module, as the solution was in Controller undeclared in Jasmine test.
What else could it be? I haven't found much about these errors on the web. I would be very happy to finally find an answer for that.
As runTarm mentioned, the path in the karma.conf.js was not set to the actual path of the script file (hello.js).