1

I am trying to test a controller which is using a service. However, the service is currently null as I want to isolate the tests in the controller.

Here is the current test, which is not working because the BoardService is null

beforeEach(inject(function($rootScope, $controller) {

    scope = $rootScope.$new();

    BoardController = $controller('BoardController', { 
        $scope: scope,
        board: {id: 1, tasks: {}},
        BoardService: null
    });

}));


it ("should add a new task", function() {

    var tasksBefore = scope.board.tasks.length;

    scope.addTask(category, 'this is a new task');

    var tasksAfter = scope.board.tasks.length;

    expect(tasksAfter).toBe(tasksBefore + 1);
});

Here is the addTask() function from the controller:

$scope.addTask = function(category, task) {

    BoardService.addTask({name : task, category : category.id}).success(function(task_id) {
        // Removed code for simplicity
    });

}

Aaaand at last, the function in the service:

this.addTask = function(data) {
    return $http.post($rootScope.serverRoot + '/task/create', data);            
}
Pixark
  • 385
  • 1
  • 4
  • 13

2 Answers2

0

I think the idea is to mock a BroadService...so maybe something like...

beforeEach(inject(function($rootScope, $controller) {

    scope = $rootScope.$new();
    var boardService = {
         addTask:function(task){
            var d = $q.defer();
            d.resolve(/*Whatever your service function returns: if its just a task_id, return an int or something*/);
            return d.promise;
         };
    };

    BoardController = $controller('BoardController', { 
        $scope: scope,
        board: {id: 1, tasks: {}},
        BoardService: boardService
    });

}));


it ("should add a new task", function() {

    var tasksBefore = scope.board.tasks.length;

    scope.addTask(category, 'this is a new task');
    scope.$apply();
    var tasksAfter = scope.board.tasks.length;

    expect(tasksAfter).toBe(tasksBefore + 1);
});
user1135469
  • 1,020
  • 11
  • 22
0

First you need to mock the BoardService and stub the function you need to test.

beforeEach(inject(function ($rootScope, $controller, $q) {
    fakeDashboardItemService = {
        addTask: function () {}
    };

    scope = $rootScope.$new();
    q = $q;
    BoardController = $controller('BoardController', {
        $scope: scope, 
        BoardService: fakeBoardService
    });
}));

Then you can spyOn that function you stubbed, set your test case, and return the promise. I'm using $digest here for speed and to avoid messing with the root scope ($apply vs $digest in directive testing)

it ("should add a new task", function() {
    spyOn(fakeBoardService, 'addTask').andCallFake(function () {
        var deferred = q.defer();
        deferred.resolve(/*return test case here*/);
        return deferred.promise;
    })

    var tasksBefore = scope.board.tasks.length;
    scope.addTask(category, 'this is a new task');
    scope.$digest();
    var tasksAfter = scope.board.tasks.length;
    expect(tasksAfter).toBe(tasksBefore + 1);
});

The final important change is that you should re-factor your controller method to use .then instead of .success. and .error. This is so that you can handle the full $http promise and not just the success and error sub-functions that won't be called when you return the promise from your test function. Note that the .then receives the entire response instead of just the data.

$scope.addTask = function(category, task) {
    BoardService.addTask({name: task, category: category.id}).then(function(response) {
        // handle success case
        var task_id = response.data;
    }, function(err) {
        // handle error case
    });
}
Community
  • 1
  • 1
bgoerdt
  • 162
  • 1
  • 7