I met a problem with testing my controllers. I load them dynamically (lazy load, see after) so I wrote them like this :
angular.module('myApp').controllerProvider.register('DashboardCtrl', [
'$scope', function ($scope) {
$scope.foo = 'bar';
}
]);
I initialise my module like this :
var app = angular.module('myApp', [
'ngRoute',
'ngCookies',
'ngResource',
'ngSanitize',
'ui.router'
]);
app.run([
'$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}
]);
app.config(function ($stateProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
app.stateProvider = $stateProvider;
app.routeProvider = $urlRouterProvider;
app.controllerProvider = $controllerProvider;
app.compileProvider = $compileProvider;
app.filterProvider = $filterProvider;
app.provide = $provide;
$urlRouterProvider.otherwise('/');
$stateProvider
.state('dashboard', {
url : '/',
templateUrl : 'views/dashboard.html',
controller : 'DashboardCtrl',
resolve : {
deps : function ($q, $rootScope) {
var deferred = $q.defer();
curl('scripts/controllers/dashboard.js')
.then(function () {
$rootScope.$apply(function () {
deferred.resolve({});
});
});
return deferred.promise;
}
}
});
});
That works. Method is described there : http://ify.io/lazy-loading-in-angularjs/
But when I want to test, I get each time the error : Error: [ng:areq] Argument 'DashboardCtrl' is not a function, got undefined
My test script :
describe('Controller: DashboardCtrl', function () {
'use strict';
var DashboardCtrl,
scope;
// load the controller's module
beforeEach(function () {
var app = angular.module('cellierApp', [
'ngRoute',
'ngCookies',
'ngResource',
'ngSanitize',
'ui.router'
]);
app.run([
'$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}
]);
app.config(function ($stateProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
app.stateProvider = $stateProvider;
app.routeProvider = $urlRouterProvider;
app.controllerProvider = $controllerProvider;
app.compileProvider = $compileProvider;
app.filterProvider = $filterProvider;
app.provide = $provide;
});
inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
DashboardCtrl = $controller('DashboardCtrl', {
$scope : scope
});
});
});
it('should attach a list of awesomeThings to the scope', function () {
expect(scope.foo).toBeDefined();
expect(scope.foo).toBe('bar');
});
});
I don't know what I'm doing wrong. Is there someone who knows ? Thanks in advance
I'm not sure whether Karma has support for Script.js loading, but for sure it has support for Require.js.
Since you followed that tutorial, have you checked out the final example he gave on github (It contains unit testing as well)? The idea is that you can convert everything to use Require.js instead of Script.js and use Karma with karma-requirejs for the testing.
Although it is not too difficult, there are quite a lot of changes. I won't post the working code here, but I would suggest reading the following:
http://ify.io/unit-testing-lazily-loaded-angularjs-artefacts/ and http://karma-runner.github.io/0.8/plus/RequireJS.html
I can't link the final example because I don't have the reputation, but it's at the bottom of the tutorial you've linked.
To see a runnable example using Asynchronous Module Definitions with RequireJS, have a look at the sample app."
You don't load modules, but override the angular modules in this snippet:
var app = angular.module('cellierApp', [
'ngRoute',
'ngCookies',
'ngResource',
'ngSanitize',
'ui.router'
]);
The proper call would be
beforeEach(function() {
// the module to be tested
module('cellierApp');
});
Also, the var declaration of the app is very crappy imo. You have angular as a container, use it, don't create objects on global closure for no reason. Change this:
var app = angular.module('myApp', [
'ngRoute',
'ngCookies',
'ngResource',
'ngSanitize',
'ui.router'
]);
app.run...
To:
angular.module('myApp', [
'ngRoute',
'ngCookies',
'ngResource',
'ngSanitize',
'ui.router'
])
.config([...
.run([....
The unit test itself can be simplified so much:
describe('Controller: DashboardCtrl', function() {
'use strict';
var scope;
// load the controller's module
beforeEach(function() {
module('cellierApp');
});
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
$controller('DashboardCtrl', {
$scope: scope
});
}));
it('should attach a list of awesomeThings to the scope', function() {
expect(scope.foo).toBeDefined();
expect(scope.foo).toBe('bar');
});
});
In your test you are not registering your controller
put your code for creating you controller in your test
app.controllerProvider.register('DashboardCtrl', [
'$scope', function ($scope) {
$scope.foo = 'bar';
}
]);
however depending on your test setup your app and controller files should be loaded through your test system and you should use
beforeEach(module('cellierApp'));
instead of creating your app directly in your test
Related
I'm trying to test my app, which has each controller defined on it's own module i.e., not as a controller of the app module, and then loaded as a dependency of the main app module. When I try to run a test that just checks that the loginController is defined, using Karma/Jasmine, I get the following output:
'Expected undefined to be defined.'
edit
I updated login.controller.spec and switched the karma browser to chrome, which gave me more useful debug info. Now I'm getting an error related to a factory that is added to $httpProvider.interceptors in the main app file:
Unknown provider: authFactoryProvider <- authFactory <- $http <- $translateStaticFilesLoader <- $translate
I found similar issues related to this which were resolved by including angular-translate-loader-static-files.js, which is being loaded when karma runs:
DEBUG [web-server]: serving (cached): /path/to/my/app/bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js
How do I resolve all these dependency issues with karma?
index.js
'use strict';
angular.module('app',
[
//'mockBackend', //uncomment when loading mock backend
'ngAnimate',
'ngCookies',
'ngTouch',
'ngSanitize',
'ngResource',
'ui.bootstrap',
'ui.router',
'ui.router.stateHelper',
'pascalprecht.translate',
'utilsModule',
'loginModule',
'vsmsPackageModule',
'vsmsCampaignModule',
'vdmsCampaignModule',
'vdmsDashboardModule',
'daterangepicker',
'ui.event',
'idmAdminModule',
'helpdeskModule',
'ncy-angular-breadcrumb',
'rzModule',
'vsmsDashboardModule',
'highcharts-ng',
'permission',
'dndLists'
])
.config(function ($stateProvider, $urlRouterProvider, $httpProvider, $locationProvider, $translateProvider, $breadcrumbProvider, $compileProvider) {
$urlRouterProvider.otherwise('/');
//initialize get if not there
if (!$httpProvider.defaults.headers.get) {
$httpProvider.defaults.headers.get = {};
}
//disable IE ajax request caching
$httpProvider.defaults.headers.get['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
// extra
$httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';
$httpProvider.defaults.headers.get['Pragma'] = 'no-cache';
$locationProvider.html5Mode({
enabled: false,
requireBase: false
});
$translateProvider.useSanitizeValueStrategy('sanitize');
$translateProvider.useStaticFilesLoader({
prefix: '/locales/',
suffix: '.json'
});
$translateProvider
.preferredLanguage('en_us')
.fallbackLanguage('en_us');
$breadcrumbProvider.setOptions({
templateUrl: 'components/templates/breadcrumb.tpl.html'
});
$compileProvider.debugInfoEnabled(false);
// $compileProvider.aHrefSanitizationWhitelist(/^\s*(|blob|):/);
$httpProvider.interceptors.push('authFactory');
$httpProvider.interceptors.push('headersFactory');
})
login.module.js
angular.module('loginModule', []);
login.controller.js
angular.module('loginModule')
.controller('loginController', login);
login.$inject = [
'$log',
'$uibModal',
'$rootScope',
'storageFactory',
'loginFactory',
'$state',
'RoleStore',
'PermissionStore'
];
function login($log, $uibModal, $rootScope, storageFactory, loginFactory, $state, RoleStore, PermissionStore) {
/* jshint validthis: true */
var vm = this;
vm.loginUser = loginUser;
vm.forgotPassword = forgotPassword;
vm.errorCode = null;
PermissionStore.clearStore();
function loginUser() {
...
I'm just trying to test if the controller exists and I can't get past the error:
Expected undefined to be defined.
login.controller.spec.js
describe('loginController', function() {
beforeEach(module('app'));
var $controller,
$scope,
$log,
$uibModal,
$rootScope,
storageFactory,
loginFactory,
$state,
RoleStore,
PermissionStore,
vsmsCoreFactory;
beforeEach(inject(function(_$controller_, _$log_, _$uibModal_, _$rootScope_, _storageFactory_, _loginFactory_, _$state_, _RoleStore_, _PermissionStore_, _vsmsCoreFactory_){
$controller = _$controller_;
$scope = $rootScope.new();
$log = _$log_;
$uibModal = _$uibModal_;
$rootScope = _$rootScope_;
storageFactory = _storageFactory_;
loginFactory = _loginFactory_;
$state = _$state_;
RoleStore = _RoleStore_;
PermissionStore = _PermissionStore_;
vsmsCoreFactory = _vsmsCoreFactory_;
}));
describe('vm.loginUser', function() {
it('should be defined', function() {
var loginController = $controller('loginController', {
$log: $log,
$uibModal: $uibModal,
$rootScope: $rootScope,
storageFactory: storageFactory,
loginFactory: loginFactory,
$state: $state,
RoleStore: RoleStore,
PermissionStore: PermissionStore,
vsmsCoreFactory: vsmsCoreFactory
});
expect(loginController).toBeDefined();
// expect(testController.model.name).toEqual("controllerAs vm test");
});
});
});
unit-tests.js
'use strict';
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var wiredep = require('wiredep');
var paths = gulp.paths;
function runTests (singleRun, done) {
var bowerDeps = wiredep({
directory: 'bower_components',
exclude: ['bootstrap-sass-official'],
dependencies: true,
devDependencies: true
});
var testFiles = bowerDeps.js.concat([
'./src/app/index.js',
'./src/components/scripts/ui-bootstrap-custom-tpls-2.1.3.js',
'./src/{app,components}/**/*.module.js',
'./src/{app,components}/**/*.factory.js',
'./src/{app,components}/**/*.controller.js',
'./src/{app,components}/**/*.spec.js'
]);
gulp.src(testFiles)
.pipe($.karma({
configFile: 'karma.conf.js',
action: (singleRun)? 'run': 'watch'
}))
.on('error', function (err) {
// Make sure failed tests cause gulp to exit non-zero
throw err;
});
}
gulp.task('test', function (done) { runTests(true /* singleRun */, done) });
gulp.task('test:auto', function (done) { runTests(false /* singleRun */, done) });
Please refer the the below link it is already answered.
How to inject controller dependencies in Jasmine tests?
loginController dependencies are not passed in your unit test.
The second parameter of $controller is for controller dependencies.
Empty dependency is passed.
$controller('loginController', {});
Make sure all the dependent modules are loaded before testing loginColtroller.
I have modified your code. I hope it will work.
'use strict';
describe('loginController', function() {
beforeEach(module('loginModule'));
var loginController;
beforeEach(inject(function($controller, _$log_, _$uibModal_, _$rootScope_, _storageFactory_, _loginFactory_, _$state_, _RoleStore_, _PermissionStore_ ){
scope = _$rootScope_.$new();
loginController = $controller('loginController',
{ // all dependencies has to be passed in order
'$log' : _$log_,
'$uibModal' : _$uibModal_,
'$rootScope' : _$rootScope_,
'storageFactory' : _storageFactory_,
'loginFactory': _loginFactory_,
'$state': _$state_,
'RoleStore': _RoleStore_,
'PermissionStore': _PermissionStore_
},
{});
}));
it('should be defined', function() {
expect(loginController).toBeDefined();
});
});
The issue is with your login.module.js file.
You'll have to inject ui.bootstrap and ui.router as dependencies while defining the loginModule. Otherwise it will not be able to get $uibModal and $state while defining the loginController.
This should be the definition of your loginModule
angular.module('loginModule', ['ui.bootstrap', 'ui.router']);
PS: I'm assuming here that storageFactory, loginFactory, RoleStore and PermissionStore are defined on loginModule itself.
Hope this helps!
I am able to lazy load angularjs with the help of requirejs. But, how can I load modules that needs to be associated to the controller?
My example configuration in app.js looks like the following, loading all the providers and keeping a reference.
var app = angular.module('myApp', ['ui.router'])
var cacheProviders = {};
app.getProvider = function () {
return cacheProviders.$provide;
}
app.getCompileProvider = function () {
return cacheProviders.$compileProvider;
}
app.getControllerProvider = function () {
return cacheProviders.$controllerProvider;
}
app.getFilterProvider = function () {
return cacheProviders.$filterProvider;
}
app.config(['$stateProvider', '$urlRouterProvider', '$controllerProvider', '$compileProvider', '$filterProvider', '$provide',
function ($stateProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
(function () {
cacheProviders.$controllerProvider = $controllerProvider;
cacheProviders.$compileProvider = $compileProvider;
cacheProviders.$filterProvider = $filterProvider;
cacheProviders.$provide = $provide;
})();
var lazyCtrlLoad = function (controllerName) {
return ["$q", function ($q) {
var deferred = $q.defer();
require([controllerName], function () {
deferred.resolve();
});
return deferred.promise;
}];
}
$stateProvider.state('main.view2b', {
url: '/view2b',
templateUrl: 'forms/empl/searchEmplForm.html',
controllerAs: 'srchC',
controller: 'searchEmplCtrl',
resolve: {
loadOtherCtrl: lazyCtrlLoad('searchEmplCtrl')
}
})
In my other module, I am trying to register controllers, load services..
define([
'angular', 'angularResource'
], function (angular) {
angular.module('myApp')
.getControllerProvider()
.register(ctrl, ...)
But, while loading service below, I need access to $resource which is part of ngResource module in angularResource.
angular.module('myApp')
.getProvider().service('deptService', ['$resource', function ($resource) {
return $resource('/dept/:dept', {dept: '#_dept'});
}])
How can I load ngResource while initalizing the javascript controllers/services lazily?
Take a look to AngularAMD here. It allows you to load controllers in the ui-router without using lazyload. This AngularAMD is used to integrate requireJs and Angular.
$stateProvider
.state('home', {
url: '',
views: {
'#': angularAmd.route({
templateUrl: 'ngApplication/application/shared/layouts/basic/basicTplView.html',
controllerUrl: 'ngApplication/application/shared/layouts/basic/basicTplCtrl.js',
controller: 'basicTplCtrl'
}),
'header#home': angularAmd.route({
templateUrl: 'ngApplication/application/shared/layouts/header/headerView.html',
controllerUrl: 'ngApplication/application/shared/layouts/header/headerCtrl.js',
controller: 'headerCtrl'
})
},
});
Also, you are using requirejs, you can load all the dependencies for an specific controller using the define syntax of requireJs. Let's say you want to create a loginCtroller in a separately file, and this controller depends on another angular service:
define(['app', 'transformRequestAsFormPostService'], function (app) {
app.controller('loginCtrl', ['$scope', '$rootScope', '$sce', '$http', '$state', 'transformRequestAsFormPostService', function ($scope, $rootScope, $sce, $http, $state, transformRequestAsFormPost) {
$scope.login = function () {
/*do something here using the service*/
};
}]);
});
Here, the dependency called transformRequestAsFormPostService is another file, I defined it in the main.js (requireJs confifguration file) and it's defined using the same approach than the loginCtrol. Now I am using it in my project and its working so far so good.
Regards,
Ernesto
I got a problem when I tried to test my controller. When I run my test I got an error
Error: [ng:areq] Argument 'MainCtrl' is not a function, got undefined http://errors.angularjs.org/1.3.8/ng/areq?p0=MainCtrl&p1=not%20a%20function%2C%20got%20undefined
at assertArg (/Users/tetianachupryna/project/bower_components/angular/angular.js:1577)
at assertArgFn (/Users/tetianachupryna/project/bower_components/angular/angular.js:1588)
at /Users/tetianachupryna/project/bower_components/angular/angular.js:8418
at /Users/tetianachupryna/project/src/spec/controllers/main-controller.spec.js:11
at /Users/tetianachupryna/project/src/spec/controllers/main-controller.spec.js:17
at /Users/tetianachupryna/project/node_modules/karma-jasmine/lib/adapter.js:184
at http://localhost:9877/karma.js:185
at http://localhost:9877/context.html:51
I know that SO is full of similar questions. But I'm a total null in Angular and JS in general, so those answers didn't help me. From similar questions on SO I discovered that my problem is in wrong definition of the controller but I still can't figure out what I did wrong. I've stack and I'm begging for your help.
First of all here is my src/app/index.js file where my module is defined
var app = angular.module('myModule', [
'ngAnimate',
'ngSanitize',
'ngResource',
'ui.router',
'pascalprecht.translate',
'thing1',
'thing2']);
Here is src/app/controllers/main-controller.js
angular.module('myModule').controller('MainCtrl', [
'$scope',
'$state',
function ($scope, $state) {
$scope.state = $state;
//***
$scope.isBigStep = function isBigStep() {
return $state.$current.step == 3;
};
}]);
And finally this a file with the test src/spec/controllers/main-controller.spec.js
describe('MainCtrl', function() {
var scope, $state, createController;
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
createController = function() {
return $controller('MainCtrl', {
'$scope': scope
});
};
}));
it('should make 3 as current step', function() {
var controller = createController();
expect(scope.isBigStep()).toBe(true);
});
});
In karma config I have all those files
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'src/app/index.js',
'src/app/controllers/*.js',
'src/spec/controllers/*.js'
],
For run my test I use karma-runner plugin in RubyMine.
I'd be thankful for any help!
What you are missing is to add the module in the beforeEach hook for test setup. Otherwise the controller will not be found. So add beforeEach(module('myModule')).
describe('MainCtrl', function() {
var scope, $state, createController;
beforeEach(module('myModule')); //<--- Hook module
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
createController = function() {
return $controller('MainCtrl', {
'$scope': scope
});
};
}));
it('should make 3 as current step', function() {
var controller = createController();
expect(scope.isBigStep()).toBe(true);
});
});
I am trying to expose restful web service using angularjs ngResource using java as my backend everything seems to be correct but don't know What is wrong with my code
nothing gets displayed in browser help me with this
service.js
'use strict';
var employeeServices = angular.module('myApp.services',['ngResource']);
employeeServices.factory('Employees',['$resource',function ($resource){
return $resource('my service link', {},{
query:{method:'GET',isArray:true}
});
}]);
Controller.js
'use strict';
angular.module('myApp.controllers',[]).
controller('Myctrl1',['$scope','Employees',function ($scope ,Employees){
$scope.allemployees = Employees.query();
}]).
controller('Myctrl2',[function (){
}]);
app.js
'use strict';
angular.module("myApp", [
'ngRoute',
'myApp.controllers',
'myApp.services'
]).
config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view1',{templateUrl:'partials/partials.html',controller:'Myctrl1'});
$routeProvider.when('/view2',{templateUrl:'partials/partials1.html',controller:'Myctrl2'});
$routeProvider.otherwise({redirectTo: '/view1'});
}]);
If you want to use service of one module in another module you have to inject it.
service.js
'use strict';
var employeeServices = angular.module('myApp.services', ['ngResource']);
employeeServices.factory('Employees', ['$resource', function ($resource) {
return $resource('my service link', {}, {
query: { method: 'GET', isArray: true }
});
}]);
Controller.js
'use strict';
angular.module('myApp.controllers', ['myApp.services']).
controller('Myctrl1', ['$scope', 'Employees', function ($scope, Employees) {
$scope.allemployees = Employees.query();
}]).
controller('Myctrl2', [function () {
}]);
app.js
'use strict';`enter code here`
angular.module("myApp", [
'ngRoute',
'myApp.controllers',
'myApp.services'
]).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/view1', { templateUrl: 'partials/partials.html', controller: 'Myctrl1' });
$routeProvider.when('/view2', { templateUrl: 'partials/partials1.html', controller: 'Myctrl2' });
$routeProvider.otherwise({ redirectTo: '/view1' });
}]);
try this
I have read through many examples in regards to injecting controllers into a jasmine unit test, however I keep getting "Error: [ng:areq] http://errors.angularjs.org/undefined/ng/areq?p0=MainCtrl&p1=not%20a%20function%2C%20got%20undefined".
Here is my code:
main.spec.js:
'use strict'
describe("Testing Main Controller", function(){
var scope, controller;
var dummyFunction = function(){};
var defaultDocument = {
_id: "123456"
};
beforeEach(module('app.controllers'));
beforeEach(module('app'));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MainCtrl', {
$scope: scope,
SearchService: dummyFunction,
ResultsService: dummyFunction,
FacetService: dummyFunction,
EsDateService: dummyFunction,
Likes: dummyFunction,
Bookmarks: dummyFunction
});
}));
describe("Likes", function(){
it('shall give the user the ability to like a document that is currently being displayed.', function(){
scope.updateLike([defaultDocument]);
expect(defaultDocument.isLiked).toBe(true);
});
it('shall give the user the ability to remove a like from a document that is currently being displayed.', function(){
defaultDocument.isLiked = true;
scope.updateLike([defaultDocument]);
expect(defaultDocument.isLiked).toBe(true);
});
});
});
main_controller.js:
'use strict';
angular.module('app.controllers')
.controller('MainCtrl', function($scope, SearchService, ResultsService, FacetService, EsDateService, Likes, Bookmarks) {
});
app.js:
angular.module('app.services', ['ngResource', 'elasticjs.service']);
angular.module('app.controllers', [ 'app.services']);
var app = angular.module('app', [
'ui.bootstrap',
'elasticjs.service',
'app.services',
'app.controllers',
'app.config',
'facet.directives',
'ngRoute']);
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider
.when('/', {
controller: 'SearchCtrl',
templateUrl: 'views/search/search.html'
})
.when('/journal', {
controller: 'JournalCtrl',
templateUrl: 'views/journal/journal.html'
})
.otherwise({
redirectTo: '/'
});
}
]);
app.config(['$locationProvider',
function($locationProvider) {
$locationProvider.hashPrefix("!");
}
]);
When I attach MainCtrl to app rather than app.controllers it seems to find MainCtrl. What am I doing wrong?
You don't need to re-declare dependencies for app module, as app module injects app.controllers
beforeEach(module('app'));
Quick example how it can be solved - http://jsfiddle.net/PtXFb/