How to inject dependencies in jasmine test for an angular item

50,557

Solution 1

The main problem with your test code is that it tries to create a controller's instance "by hand" using the new operator. When doing so AngularJS has no chance to inject dependencies. What you should be doing is to allow AngularJS inject dependencies:

var $scope, ctrl;

//you need to inject dependencies first
beforeEach(inject(function($rootScope) {
    $scope = $rootScope.$new();        
}));

it('Should initialize value to Loading', inject(function($controller) {
    ctrl = $controller('MainNavController', {
        $scope: $scope
    });
    expect($scope.wksp_name).toBe('Loading...');
}));

Here is the link to a complete jsFiddle: http://jsfiddle.net/pkozlowski_opensource/7a7KR/3/

There are 2 things worth noting in the above example:

  1. You can use the inject() method from the ngMock module to inject dependencies: https://docs.angularjs.org/api/ngMock/function/angular.mock.inject
  2. To create a controller instance (that supports dependency injection) you would use the $controller service: http://docs.angularjs.org/api/ng.$controller

As the last remark: I would advise naming controllers starting with an uppercase letter - this way we won't confuse them with variable names.

Solution 2

Great answer by @pkozlowski.opensource. To elaborate a bit more... Sometimes it could be also handy to assert that $scope.$on was really called by your controller. In this case you can spy on $scope.$on as pointed out below:

beforeEach(inject(function($rootScope) {
    $scope = $rootScope.$new();        
    spyOn($scope, '$on').andCallThrough();
}));

And then you can assert that $on was called with your event name and some function as arguments:

it('Should bind to "broadCastWkspNameEvent"', inject(function($controller) {
    ctrl = $controller('MainNavController', {
        $scope: $scope
    });
    expect($scope.$on).toHaveBeenCalledWith('broadCastWkspNameEvent', jasmine.any(Function));
}));

Solution 3

I agree with pkozowski's response, but to answer your question more directly, you need to stub out '$on'

Your example would pass if your $scope looked like:

$scope = {
  $on: function() {}
}
Share:
50,557
Ranjith Ramachandra
Author by

Ranjith Ramachandra

Updated on February 16, 2020

Comments

  • Ranjith Ramachandra
    Ranjith Ramachandra over 4 years

    Here is the test spec file:

    describe('Test main controller', function(){
            it('Should initialize value to Loading', function(){
                $scope = {}
                ctrl =  new mainNavController($scope)
                expect($scope.wksp_name).toBe('Loading')
            })
        })
    

    Here is the controller file

    function mainNavController($scope) {
        $scope.wksp_name = 'Loading...'
        $scope.$on('broadCastWkspNameEvent', function (e, args) {
            $scope.wksp_name = args
        })
    }
    
    mainNavController.$inject=['$scope']
    

    But my test fails saying Object #<Object> has no method '$on'

    I am using the basic setup of jasmine.

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
      "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
      <title>Jasmine Spec Runner</title>
    
      <link rel="shortcut icon" type="image/png" href="testlib/jasmine-1.2.0/jasmine_favicon.png">
      <link rel="stylesheet" type="text/css" href="testlib/jasmine-1.2.0/jasmine.css">
      <script type="text/javascript" src="testlib/jasmine-1.2.0/jasmine.js"></script>
      <script type="text/javascript" src="testlib/jasmine-1.2.0/jasmine-html.js"></script>
    
      <!-- include source files here... -->
      <script type="text/javascript" src="/static_files/js/test-specs/main-nav-spec.js"></script>
    
      <!-- include spec files here... -->
      <script type="text/javascript" src="/static_files/js/common/jquery/latest.js"></script>
      <script type="text/javascript" src="/static_files/js/common/angular/angular-1.0.1.min.js"></script>
      <script type="text/javascript" src="/static_files/js/common/angular/angular-resource-1.0.1.min.js"></script>
      <script type="text/javascript" src="/static_files/js/section/main-nav-controller.js"></script>
    
      <script type="text/javascript">
        (function() {
          var jasmineEnv = jasmine.getEnv();
          jasmineEnv.updateInterval = 1000;
    
          var htmlReporter = new jasmine.HtmlReporter();
    
          jasmineEnv.addReporter(htmlReporter);
    
          jasmineEnv.specFilter = function(spec) {
            return htmlReporter.specFilter(spec);
          };
    
          var currentWindowOnload = window.onload;
    
          window.onload = function() {
            if (currentWindowOnload) {
              currentWindowOnload();
            }
            execJasmine();
          };
    
          function execJasmine() {
            jasmineEnv.execute();
          }
    
        })();
      </script>
    
    </head>
    
    <body>
    </body>
    </html>
    

    What is it that I am doing wrong? I am not able to understand how this thing is supposed to work :)