Richard VanBreemen
Richard VanBreemen

Reputation: 573

Testing Angular controller and spying on a mocked service

So I'm learning how to test in Angular and followed a tutorial that showed how to mock out the services that your controller uses. Can't seem to figure out why it isn't working so I'm turning here for help. Any advice on what I'm doing wrong and how to get it working would be greatly appreciated.

Error: Expected a spy, but got Function.

// Controller

'use strict';

angular.module('tikrApp')
  .controller('MessageCtrl', ['$scope', '$state', 'messageService', function ($scope, $state, messageService) {

    /**
     * Set $state on the scope to access it in the views
     */
    $scope.$state = $state;

   /**
    * Fetches a messages list that belongs to the authenticated user
    */
   $scope.inbox = function () {
     messageService.inbox().then(function (messages) {
       $scope.messages = messages;
     });
   };

  /**
   * Fetches a message
   */
  $scope.show = function (message) {
    messageService.update(message, { read: true }).then(function (result) {
      if (result) {
        $scope.message = message;
        $scope.message.read = true;
      }
    });
  };

  /**
   * Prioritizes the message for the user
   */
  $scope.starred = function (message) {
    messageService.update(message, { starred: true }).then(function (result) {
      if (result) message.starred = true;
    });
  };

  /**
   * Creates a new private message to a user
   */
  $scope.create = function (newMessage) {
    messageService.create(newMessage).then(function () {
      $scope.messages.push(newMessage);
      $state.transitionTo('inbox.messages');
    }, function () {
      $state.transitionTo('inbox.messages.create');
    });
  };

  $scope.inbox();
  // $state.transitionTo('inbox.messages');

}]);

// Spec

'use strict';

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

  var scope;
  var messageService;

  beforeEach(function () {

    var mockMessageService = {};
    module('tikrApp', function ($provide) {
      $provide.value('messageService', mockMessageService);
    });

    inject(function ($q) {
      mockMessageService.messages = [
        { to: '123', from: '123', title: 'Title 1', content: 'Content 1', read: false },
        { to: '123', from: '123', title: 'Title 2', content: 'Content 2', read: false },
        { to: '123', from: '123', title: 'Title 3', content: 'Content 3', read: false }
      ];

      mockMessageService.inbox = function () {
        var defer = $q.defer();
        defer.resolve(this.messages);
        return defer.promise;
      };

      mockMessageService.create = function (newMessage) {
        var defer = $q.defer();
        defer.resolve();
        return defer.promise;
      };

    });

  });

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

    scope = $rootScope.$new();
    messageService = _messageService_;
    $controller('MessageCtrl', {
      $scope: scope,
      messageService: messageService
    });

    scope.$digest();

  }));

  it('should list all messages in the users inbox', function () {
    // tried this way
    spyOn(scope, 'inbox');
    // and this way
    spyOn(messageService, 'inbox');
    expect(messageService.inbox).toHaveBeenCalled();
  });

});

Upvotes: 0

Views: 999

Answers (1)

Daniil Moskovtsov
Daniil Moskovtsov

Reputation: 328

This's how I usually unit test controller-service communication. I find this approach easier:

'use strict';
describe('countryController', function () {
    var scope;
    var searchService;

    beforeEach(module('app'));
    beforeEach(inject(function ($controller, $rootScope) {
        scope = $rootScope.$new();
        mockSearchService();
        createController($controller);
    }));

    it('loads flights after view is shown', function () {
        scope.loadFlights();
        expect(searchService.loadFlights).toHaveBeenCalled();
    });

    function mockSearchService() {
        searchService = jasmine.createSpyObj('searchService', ['loadFlights']);
    }

    function createController($controller) {
        $controller('countryController', {
            $scope: scope,
            searchService: searchService
        });
    }
});

Upvotes: 1

Related Questions