Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jasmine angularjs - spying on angular-local-storage methods called when controller is initialized

Similar question to: Jasmine angularjs - spying on a method that is called when controller is initialized

In my controller I use the angular-local-storage package to provide a localStorageService through injection.

in my unit tests I want to make sure the data is retrieved from the service, so I spy on the "get" and "add" methods and mock them (.andCallFake).

this works fine with all the methods that are called through $scope.$watch - as long as I force a $digest. but for the method that is called on the controller initialization, it does not seem to work. Can anyone advise why this doesn't work?

app>Main.js

angular.module('angularTodoApp')
  .controller('MainCtrl',['$scope','localStorageService',function ($scope, localStorageService) {

    var todosInStore =  localStorageService.get('todos');
    $scope.todos = todosInStore && todosInStore.split('\n') || [];
    //$scope.todos = ['Item 1', 'Item 2', 'Item 3'];

    $scope.$watch('todos', function(){
      localStorageService.add('todos', $scope.todos.join('\n'));
    },true);

    $scope.addTodo  = function() {
      $scope.todos.push($scope.todo);
      $scope.todo = '';
    };

    $scope.removeTodo = function(index) {
      $scope.todos.splice(index,1);
    };
  }]);

test>Main.js

describe('Controller: MainCtrl', function () {

  // load the controller's module
  beforeEach(module('angularTodoApp'));

  var MainCtrl,
    scope,
    localStorageService,
    store = [];

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope, _localStorageService_) {
    scope = $rootScope.$new();
    localStorageService = _localStorageService_;
    MainCtrl = $controller('MainCtrl', {
      $scope: scope,
      localStorageService: _localStorageService_
    });

    //mock localStorageService get/add
    spyOn(localStorageService,'get').andCallFake(function(key){
      return store[key];
    });
    spyOn(localStorageService,'add').andCallFake(function(key, val){
      store[key] = val;
    });
  }));

  it('should retrieve "todos" from the store and assign to scope', function () {
    expect(localStorageService.get).toHaveBeenCalledWith('todos');
    expect(scope.todos.length).toBe(0);
  });

  it('should add items to the list and update the store for key = "todos"', function () {
    scope.todo = 'Test 1';
    scope.addTodo();
    scope.$digest();
    expect(localStorageService.add).toHaveBeenCalledWith('todos', jasmine.any(String));
    expect(scope.todos.length).toBe(1);
  });

all tests pass except the one in the constructor:

expect(localStorageService.get).toHaveBeenCalledWith('todos');
like image 856
Vincent De Smet Avatar asked Jan 10 '23 19:01

Vincent De Smet


1 Answers

The reason is that the controller is initialised at the point you call $controller, which in your example is before you're created the spies via spyOn. The solution for your example is to move the call to $controller to after the calls to spyOn.

For longer test suites, to keep things DRY, you might have to put the call to $controller in a separate function, that you can then call after you have mocked up any required services.

like image 174
Michal Charemza Avatar answered Jan 26 '23 13:01

Michal Charemza