Error initializing controller while testing angularjs with karma - javascript

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).

Related

testing using karma failed : Module app is not available

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.

Controller undefined when declared in a single module

There is something I don't fully understand with AngularJS. If I declare the controllers this way:
angular.module('ngApp', []).controller('AboutCtrl', ['$scope', function ($scope) {
$scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma'];
}]);
angular.module('ngApp', []).controller('ContactCtrl', ['$scope', function ($scope) {
$scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma'];
}]);
angular.module('ngApp', []).controller('MainCtrl', ['$scope', function ($scope) {
$scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma'];
}]);
...whenever I try to test them only one of them pass the test case(s):
describe('MainCtrl', function () {
var scope;
beforeEach(module('ngApp'));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
$controller('MainCtrl', { $scope: scope });
}));
it('should attach a list of `awesomeThings` to the scope', function () {
expect(scope.awesomeThings.length).toBe(3);
});
});
(I have also three files with the same test case repeated, the only thing that changes is the controller name: AboutCtrl and ContactCtrl in the apprpriate locations).
On the other hand, if I say: var app = angular.module('ngApp', []); and then "attach" the controllers to it, everything works fine. The same if I attached them to the same chain in one angular.module('ngApp', []).controller(/* ... */).controller(/* ... */);
The reason I want them separate is because I want to have them in (three...N) different files.
Anyone has any clue?
You are re-declaring your ngApp module each time, rather than getting the already declared module.
From John Papa's Angular Style Guide:
•Only set once and get for all other instances.
Why?: A module should only be created once, then retrieved from that point and after.
/* recommended */
// to set a module
angular.module('app', []);
// to get a module
angular.module('app');
By calling angular.module('ngApp', []) you are creating a new module, not reusing the same one. In order to do that just call .module with the app name and no other argument
angular.module('ngApp')
file1.js
angular.module('ngApp').controller(...);
file2.js
angular.module('ngApp').controller(...);
you could also just save the module to a global variable and just use that variable
main.js
var app = angular.module('ngApp',[]);
file1.js
app.controller(...);
file2.js
app.controller(...);

Angular - Mocha - Tests Fail when I add multiple Controllers to the same Module

When I have one controller attached to a module, I can use mocha, karma to test it successfully. But when I add two controllers to the same module, the tests fail. Why is that?
I have 2 controllers defined on the same module. I can manually test the controllers and they work.
src/app/itemListController.js
angular.module('lazyLoad', [])
.controller('ItemListController', ['$scope', function ($scope) {
...
}]);
src/app/invoiceController.js
angular.module('lazyLoad', [])
.controller('InvoiceController', ['$scope', function ($scope) {
...
}]);
And 2 unit-tests:
test/app/itemListController.mocha.js
'use strict';
describe('testing movies', function () {
var scope;
var fixture;
beforeEach(module('lazyLoad'));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
fixture = $controller('ItemListController', {$scope: scope});
}));
it('....', function() {});
});
test/app/invoiceController.mocha.js
'use strict';
describe('testing movies', function () {
var scope;
var fixture;
beforeEach(module('lazyLoad'));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
fixture = $controller('InvoiceController', {$scope: scope});
}));
it('....', function() {});
});
I get:
PhantomJS 1.9.8 (Mac OS X 0.0.0) testing movies "before each" hook: workFn FAILED
the object {
"line": 1761
"message": "[ng:areq] Argument 'ItemListController' is not a function, got undefined
http://errors.angularjs.org/1.4.1/ng/areq?p0=ItemListController&p1=not%20a%20function%2C%20got%20undefined"
"name": "Error"
Now if I change the module-name for the invoiceController.js and invoiceController.mocha.js to say invoiceM then both tests work.
I must be doing something wrong...
You define your modules twice. When you use brackets [] to pass empty dependencies, you actually create a module and replace an old one if exists with the same name. What you need to do is:
// Create the module, maybe in a separate place.
angular.module('lazyLoad', []);
// Attach controllers to that module:
angular.module('lazyLoad') // HEY! SEE THIS? NO BRACKETS.
.controller('ItemListController', ...);]
angular.module('lazyLoad') // HEY! SEE THIS? NO BRACKETS.
.controller('InvoiceController' ...);

Karma error Argument 'Controller' is not a function, got undefined

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);
});
});

Unit testing angular/Ionic project

I have a very simple controller that looks like this.
timeInOut.controller('timeInOutController', function($scope, $filter, $ionicScrollDelegate){
...
});
Whenever I try to create a unit test for it like so...
(function() {
'use strict';
var scope, controller, filter;
describe('timeInOutController', function () {
beforeEach(module('common.directives.kmDateToday'));
beforeEach(inject(function ($rootScope, $controller, $filter) {
scope = $rootScope.$new();
filter = $filter;
controller = $controller('timeInOutController', {
$scope: scope
});
}));
describe('#date setting', function(){
...
});
});
})();
I get the error:
[$injector:unpr] Unknown provider: $ionicScrollDelegateProvider <- $ionicScrollDelegate
Obviously in my example here I'm not trying to inject the $ionicScrollDelegate into the test, that's just because I've tried it any number of ways with no success and don't know which failed attempt to include.
Also in my karma.conf.js file I am including the ionic.bundle.js and angular-mocks.js libraries/files.
I can successfully unit test anything that doesn't use anything $ionic in it, so I know my testing framework is set up correctly, the issue is injecting anything ionic related.
You need to pass in all the parameters if you're going to instantiate your controller via angular. By adding the parameters you are telling angular that any time you create one of these controllers I need these things too because I am dependent upon them.
So my suggestion is to mock up some representation of these dependencies and inject them in when you are creating the controller. They do not have to be (and should not be) the actual services for your unit tests. Jasmine gives you the ability to create spy objects that you can inject so you can verify the the behavior of this unit.
(function() {
'use strict';
var scope, controller, filter, ionicScrollDelegate;
describe('timeInOutController', function () {
beforeEach(module('common.directives.kmDateToday'));
beforeEach(inject(function ($rootScope, $controller, $filter) {
scope = $rootScope.$new();
filter = $filter;
// func1 and func2 are functions that will be created as spies on ionicScrollDelegate
ionicScrollDelegate = jasmine.createSpyObj('ionicScrollDelegate', ['func1', 'func2']
controller = $controller('timeInOutController', {
$scope: scope,
$filter: filter,
$ionicScrollDelegate: ionicScrollDelegate
});
}));
describe('#date setting', function(){
...
});
});
})();
You can find more about spies via jasmine's documentation
You need to create mock objects for all dependencies your controller is using.
Take this controller as an example:
angular.module('app.module', [])
.controller('Ctrl', function($scope, $ionicLoading) {
$ionicLoading.show();
});
Here you are using the $ionicLoading service, so if you want to test this controller, you have to mock that object specifying the methods you're using in the controller:
describe('Test', function() {
// Mocks
var $scope, ionicLoadingMock;
var ctrl;
beforeEach(module('app.module'));
beforeEach(function() {
// Create $ionicLoading mock with `show` method
ionicLoadingMock = jasmine.createSpyObj('ionicLoading', ['show']);
inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('Ctrl', {
$scope: $scope,
$ionicLoading: ionicLoadingMock
});
});
});
// Your test goes here
it('should init controller for testing', function() {
expect(true).toBe(true);
});
});

Categories

Resources