2

I'm writing an application with AngularJs 1.4.4 and have just embarked on TDD for the first time. I'm using Karma with Jasmine and have had no trouble testing expressions on the $scope, however when trying to test an expression defined using 'this' in a Controller it returns as undefined. Angular indicates that using 'this' in your controller is best practise but I've not found a clear example of testing.

Here is my Controller

'user_strict';
var app = angular.module('app', ['ngRoute', 'ngAnimate']);

angular.module('app')
app.controller('LoginCtrl', ['$scope', function($scope) {

    var login = this;
  
    login.user = {message:'hello'};
  
    $scope.userName = "Anthony";
  
  }])

My test script

'use strict';

describe('Controller: LoginCtrl', function() {
 
 // load the controller's module
 beforeEach(module('app'));

 var LoginCtrl,
 scope;

 // initalize the controller and a mock scope
 beforeEach(inject(function ($controller, $rootScope) {
  scope = $rootScope.$new();
  LoginCtrl = $controller('LoginCtrl', {
   $scope: scope,
  });

 }));

 it('should equal to equal to Anthony', function() {
  expect(scope.userName).toBe("Anthony");
 });

 it('login user should equal to hello', function() {
  expect(login.user.message).toBe('hello');
 })
});

The first test passes but the second return this error/fail;

Controller: LoginCtrl login user should equal to hello FAILED

TypeError: 'undefined' is not an object (evaluating 'login.user.message')

My presumption is that it needs to be injected like the controller and the scope but the methods I've tried haven't worked. Any help is much appreciated :)

2 Answers2

2

Using this in controller is a so called "controller as" pattern, which is described briefly in official docs.

Consider this code:

app.controller('LoginCtrl', ['$scope', function($scope) {
  var login = this;
  login.user = {message:'hello'};
  $scope.userName = "Anthony";
}]);

Here, function ($scope) { ... } is a constructor for you controller and this inside constructor references to an object, which will be created when executing constructor. That object will hold everything you've assinged to it using this. When you create controller in your code with the code

LoginCtrl = $controller('LoginCtrl', { $scope: scope });

the variable LoginCtrl holds that constructed object. You can reference it's properties, assigned with this, through the LoginCtrl variable. So basically your test should be changed to:

it('login user should equal to hello', function() {
  expect(LoginCtrl.user.message).toBe('hello');
})

Credits to Q/A accessing $scope from unit test file when using the vm "ControllerAs" syntax from AngularJS HotTowel, where you can find more info.

Community
  • 1
  • 1
Michael Radionov
  • 12,859
  • 1
  • 55
  • 72
1
var login = this;

In JavaScript variable's are local to function scope.They are not accessible outside the function.

You are trying to do same things.Thats way you getting TypeError undefined.

Do it in this way.

$scope.login = this;

$scope.login.user = {message:'hello'};

After this login is available through $scope

RIYAJ KHAN
  • 15,032
  • 5
  • 31
  • 53