r.bhardwaj r.bhardwaj - 1 month ago 12
AngularJS Question

Getting error while using Jasmine with AngularJS

I am using jasmin to test my AngularJs project with following code


controllersSpec.js

describe('myApp', function(){

beforeEach(angular.mock.module('myApp'));

it('should have a mailctrl', function() {
expect(myApp.mailctrl).toBeDefined();
});
});


controller.js


var myApp = angular.module('myApp', []);

myApp.controller('mailctrl', ['$scope', '$routeParams', '$rootScope', 'getMailData', '$angularCacheFactory',

function ($scope, $routeParams, $rootScope, getMailData, $angularCacheFactory) {
##....controller content....##
}
]);


SpecRunner.html

<script type="text/javascript" src="../lib/angular/angular.min.js"></script>
<script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-2.0.0/jasmine-html.js"></script>
<script type="text/javascript" src="lib/jasmine-2.0.0/boot.js"></script>
<script type="text/javascript" src="../test/lib/angular/angular-mocks.js"></script>
<script src="../js/controller.js"></script>
<script src="../test/unit/controllersSpec.js"></script>


Now when I am opening Specrunner.html in browser, I am getting following error


Expected undefined to be defined.

I have following question regarding above


1) Where I am getting wrong ?

2) How can I use $rootScope variable of controller ?

3) Whats the difference in beforeEach(angular.mock.module('myApp')); and beforeEach(angular.module('myApp')); and beforeEach(module('myApp'));

EDIT :


I have modified the code as

describe('myApp', function(){
beforeEach(angular.mock.module('myApp'));
it('should have a mailctrl', inject(function($rootScope, $controller) {
var controller = $controller('mailctrl', { $scope: $rootScope.$new(), $rootScope: $rootScope });
expect(controller).toBeDefined();
}));


and getting following error -


Error: [$injector:unpr] http://errors.angularjs.org/undefined/$injector/unpr?p0=%24routeParamsProvider%20%3C-%20%24routeParams

If I add other params in $controller() (i.e. '$routeParams', 'getMailData') Error comes as they are undefined.

Answer

1) You are making a mistake by trying to access your controller as a field on a module. in your test you should be injecting $controller into your test to create your controller. What that means is that you should be writing your tests like the following:

it('should have a mailctrl', inject(function($rootScope, $controller) {
   var controller = $controller('mainctrl', { $scope: $rootScope.$new(), $rootScope: $rootScope, ...  }) // rest of your dependencies
   expect(controller).toBeDefined();
}));

2) See 1

3) angular.mock.module is used to register your module to the injector in your tests. It is the same thing as module. angular.module is used to create, register or retrieve a module for your application.

Regarding your edit:

You are not providing routeParams and any other service that your controller depends on so $injector does not know where to get them from and is throwing this error. $injector has to know what to inject for every parameter that is required by your controller. Your controller depends on

  • $rootScope: provided by angular
  • $scope: can be created by $rootScope.$new()
  • getMailData: This is your service, you have to provide a mock for this
  • $routeParams: This is angular built-in structure but there is not a dedicated mock, but since it is a very simple object you can provide a mock for this yourself.
  • $angularCacheFactory: this is a third party service and you have to provide a mock for this as well

So in the end you should be creating your controller like this:

it('should have a mailctrl', inject(function($rootScope, $controller) {
   var controller = $controller('mainctrl', { 
         $scope: $rootScope.$new(), 
         '$routeParams': {}, // whatever  it needs to be to be able to test your controller
         $rootScope: $rootScope, 
         'getMailData': getMailDataMOCK // this object is created by you in your test code
         '$angularCacheFactory': $angularCacheFactoryMOCK
       }) 
   expect(controller).toBeDefined();
}));

If you don't provide any of these arguments then it will try to get it from $injector and will throw an exception when not found.

Comments