Search code examples
javascriptangularjsjasminekarma-jasminespyon

Angular Service becomes undefined when spyOn it


I have an Angular 1.6.6 application which I'm testing with Karma and Jasmine.

Given this code from controller:

    $scope.undo = function () {
        return $scope.isUndoDisabled() || $scope.undoAction();
    };

    $scope.isUndoDisabled = function () {
        return HistoryService.isUndoDisabled();
    };

I have been testing it with the following spec:

it('undoAction should not be called with default settings', function () {
        var $scope = {};
        var controller = $controller('PaintController', { $scope: $scope });

        spyOn($scope, 'undoAction');
        //spyOn(HistoryService, 'isUndoDisabled');

        $scope.undo();
        expect($scope.undoAction).not.toHaveBeenCalled();
    });

And is passing the test, but when I uncomment the spyOn of HistoryService, the call HistoryService.isUndoDisabled() from $scope.isUndoDisabled returns undefined and then the test fails because:

Expected spy undoAction not to have been called.

Any idea about what's going on???? It seems like the spyOn is affecting to the code??


Solution

  • spyOn(...) is a shortcut for spyOn(...).and.stub(), not spyOn(...).and.callThrough(). When being spied this way, HistoryService.isUndoDisabled() returns undefined.

    The proper way to test the unit is to isolate it from others. Since it is the controller that is tested, the service should be mocked or stubbed:

    spyOn(HistoryService, 'isUndoDisabled').and.returnValue(true);
    

    And then in another test:

    spyOn(HistoryService, 'isUndoDisabled').and.returnValue(false);