Jasmine - Check if service method within a controller method was called

I started testing my ionic/angularjs app with Jasmine one day ago. I'm not sure if I totally missunderstood the idea of testing, but I want to test if a service method within a controller method was called and how the controller reacts on what came back.

My controller function I'd like to test looks like this:

$scope.init = function() {
    DataService.fetchValues('dataprotection').then(function (result) {
        $scope.dataprotection = result;
    }, function (failure) {
        $scope.dataprotection = 'No dataprotection available';
    });
};

My Test should look like this:

describe('DataprotectionController', function () {
    beforeEach(inject(function ($rootScope, $controller, DataService) {
        scope = $rootScope.$new();
        controller = $controller('DataprotectionCtrl', {
            '$scope': scope
        });

        dataService = DataService;
    }));

    it('Should init dataprotection on startup', function () {
        // call init function in controller
        scope.init();
        //check if dataservice.fetchValues have been called with 'dataprotection' as parameter
        expect(dataService, 'fetchValues').toHaveBeenCalledWith('dataprotection');
        //calling fetchValues should init scope.dataprotection variable
        expect(scope.dataprotection).toBeDefined(); 
    });
});

Of course this is not working. Errorlogs tell me to create a spy object. So I did...

spyOn(dataService, 'fetchValues').andCallThrough();

Didn't help so I'm calling

dataService.fetchValues('dataprotection');

right after "scope.init();". First expect passes.

The thing I really don't understand is: why am I creating a spy object for the dataService fetchValues() method and after that call it with a parameter and after that ask if it was called with the given parameter? I don't want to call it manually, I want to check if dataService.fetchValues('dataprotection') was called inside the scope.init() function in the DataprotectionController.

Sorry if this is a really stupid question, but I totally got lost... Thanks for your help!

The following syntaxes are for Jasmine 2.0, so if you are using Jasmine 1.3 you need to make some small changes.

First of all you need to inject the DataService into the controller:

var $scope,
    DataService,
    $q;

beforeEach(module('myApp'));

beforeEach(inject(function($controller, $rootScope, _DataService_, _$q_) {

  $scope = $rootScope.$new();
  DataService = _DataService_;

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

  $q = _$q_;
}));

Note that if you use and.callThrough() the spy will delegate the function call to the real fetchValues implementation, unless you have replaced it with a mocked function yourself.

You can instead use and.callFake to return a promise:

spyOn(DataService, 'fetchValues').and.callFake(function(input) {
    var deferred = $q.defer();
    deferred.resolve('mock');
    return deferred.promise;
});

Otherwise the following code in your controller wouldn't return anything:

DataService.fetchValues('dataprotection')

Which means it would try to do the following on undefined:

.then(function(result) { ...

When using ngMock you need to synchronously maintain the flow of the tests, so after calling init you need to manually trigger a digest to get the promise to resolve:

$scope.init();

$scope.$digest();

And finally the syntax for verifying that the service function was called is:

expect(DataService.fetchValues).toHaveBeenCalledWith('dataprotection');

Demo: http://plnkr.co/edit/Pslme1Ve1M1E6hbm1J6D?p=preview