Terry
Terry

Reputation: 14219

AngularJS controller instantiation in unit tests - functions not binding to scope values

I have a service that synchronously returns data to a controller:

angular.module('app').controller(function($scope, myService) {
  $scope.foo = myService.getFoo();
});

This works just fine in the browser. In my unit tests, $scope.foo is undefined:

beforeEach(function () {
  module('app');
  myService = jasmine.createSpyObj('myService', ['getFoo']);

  inject(function($controller, $rootScope) {
    $scope = $rootScope.$new();
    ctrl = $controller('ModelSliderCtrl', {
      myService: myService,
      $scope: $scope
    });
  });
});

it('should have foo on the scope', function() {
  myService.getFoo.and.returnValue({});

  expect(myService.getFoo).toHaveBeenCalled();  // PASS
  $scope.$digest();
  expect($scope.foo).toBeDefined();  // FAIL - $scope.foo is undefined
});

This does work in both the browser and tests:

angular.module('app').controller(function($scope, myService) {
  $scope.init = function() {
    $scope.foo = myService.getFoo();
  };

  $scope.init();
});

.

it('should have foo on the scope', function() {
  myService.getFoo.and.returnValue({});

  $scope.init();
  expect(myService.getFoo).toHaveBeenCalled();  // PASS
  expect($scope.foo).toBeDefined();  // PASS
});

I'd like to believe I'm fairly well-versed in Angular, Jasmine and JavaScript. I've also asked some colleagues who are equally puzzled.

Can anyone explain to me what is going on here?

Upvotes: 0

Views: 431

Answers (1)

mpm
mpm

Reputation: 20155

You are setting up a mock

it('should have foo on the scope', function() {
  myService.getFoo.and.returnValue({});

after your controller has been instantiated. It's too late to set up the mock by then, do it before instantiating your controller since you are executing init() right away.

  myService = jasmine.createSpyObj('myService', ['getFoo']);
  myService.getFoo.and.returnValue({});

  inject(function($controller, $rootScope) {

Upvotes: 1

Related Questions