Dave Cooper
Dave Cooper

Reputation: 10714

Unit test controller with service in it

I'm trying to unit test a controller with a service injected into it. No matter what I seem to try, I get an error. Any assistance to help me get this going would be much appreciated. I'm using Angular/Karma/Jasmine to get run my tests.

There seem to be a lot of posts with similar stories but this feels like it may not be a duplicate - apologies if it is.

My controller looks like this:

(function() {
  angular
    .module('blah')
    .controller('AdminController', AdminController);

  /* @ngInject */
  function AdminController($scope, toastr, adminService) {
    activate();

    /**
     * Controller initialisation.
     */
    function activate() {
      getAllUsers();
    }

    /**
     * Gets all users.
     */
    function getAllUsers() {
      adminService.getAllUsers()
        .then(function(response) {
          $scope.users = response.data;
        })
        .catch(function(error) {
          toastr.error('Unable to load users', 'Error');
          console.log(error);  
        });
     }        
  }
})();

And my service looks like this:

(function() {
  angular
    .module('blah')
    .factory('adminService', adminService);

  /* @ngInject */
  function adminService($http, environmentConfig) {
    var service = {
      getAllUsers: getAllUsers
    };

    return service;

    /**
     * Gets all user objects.
     */
    function getAllUsers() {
      return $http.get(environmentConfig.apiBaseUrl + '/user');
    }
  }
})();

and my unit tests look like this:

describe('AdminController', function() {
  var ctrl,
      adminService,
      $scope;

  var listOfTestUsers = [
    { name: 'Dave', id: 1 },
    { name: 'Bob', id: 2 },
    { name: 'Bill', id:3 }
  ];

  beforeEach(function() {
    module('blah');
  });

  beforeEach(inject(function($rootScope, $controller) {
    adminService = {
      getAllUsers: function() {}
    };

    spyOn(adminService, 'getAllUsers').and.returnValue(listOfTestUsers);

    $scope = $rootScope.$new();

    ctrl = $controller('AdminController', {
      $scope: $scope,
      adminService: adminService
    });
  }));

  describe('The getAllUsers function should exist', function() {
    it('should work', function() {
      expect(ctrl).toBeDefined();
    });
  });
});

I get this error when running my Jasmine tests with Karma:

TypeError: adminService.getAllUsers(...).then is not a function

Upvotes: 1

Views: 1263

Answers (3)

SiddAjmera
SiddAjmera

Reputation: 39482

Here are a few things that I found wrong with the code.

  1. .catch was used earlier. .then is called with two callbacks, a success callback and an error callback. So that's what I've done in your call to adminService.getAllUsers.

  2. For the TypeError: adminService.getAllUsers(...).then is not a function that you were getting. You didn't mock getAllUsers properly. I've done that in the testCases file. It returns a function named then which was not available earlier.

Controller

(function() {
  angular
    .module('blah', [])
    .controller('AdminController', AdminController);

  /* @ngInject */
  function AdminController($scope, toastr, adminService) {
    $scope.greeting = "Hello World!";

    /**
     * Gets all users.
     */
    $scope.getAllUsers = function() {
      adminService.getAllUsers()
        .then(function(response) {
          $scope.users = response.data;
        }, function(error) {
          toastr.error('Unable to load users', 'Error');
          console.log(error);
        });
    }

    activate();

    /**
     * Controller initialisation.
     */
    function activate() {
      $scope.getAllUsers();
    }
  }
})();

environmentConfig Constant. Replace this with yours.

(function() {
  angular.module('blah').constant('environmentConfig', {
    apiBaseUrl: 'https://www.something.com'
  })
})();

toastr Service. Replace this with yours

(function() {
  angular
    .module('blah')
    .factory('toastr', toastr);

  /* @ngInject */
  function toastr() {
    var service = {
      error: error
    };

    return service;

    /**
     * Gets all user objects.
     */
    function error(a, b) {
      console.log("Here's the error : ", a);
    }
  }
})();

adminService

(function() {
  angular
    .module('blah')
    .factory('adminService', adminService);

  /* @ngInject */
  function adminService($http, environmentConfig) {

    /**
     * Gets all user objects.
     */
    function getAllUsers() {
      return $http.get(environmentConfig.apiBaseUrl + '/user');
    }

    var service = {
      getAllUsers: getAllUsers
    };

    return service;
  }
})();

Test Cases

describe('controller: AdminController', function() {
  var scope, $scope, toastr, adminService, AdminController, flag, $q;
  flag = 'success';

  var listOfTestUsers = [{
    name: 'Dave',
    id: 1
  }, {
    name: 'Bob',
    id: 2
  }, {
    name: 'Bill',
    id: 3
  }];

  beforeEach(module('blah'));

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

    scope = $rootScope.$new();
    toastr = _toastr_;
    adminService = _adminService_;
    $q = _$q_;


    spyOn(adminService, 'getAllUsers').and.callFake(function() {
      return flag === 'success' ? $q.when(listOfTestUsers) : $q.reject("Error");
    });

    AdminController = $controller('AdminController', {
      $scope: scope,
      toastr: _toastr_,
      adminService: _adminService_
    });
  }));

  describe('The getAllUsers function should exist', function() {
    it('should work', function() {
      expect(AdminController).toBeDefined();
    });
  });
});

Hope this helps.

Upvotes: 3

BoxBet
BoxBet

Reputation: 21

Your actual error is the fact that your mock service does not have the function 'getAllUsers' here is a paste bin with the adjustments http://pastebin.com/LwG0CzUW

If you prefer you could adjust your test to call the actual service as the following pastebin.

http://pastebin.com/RSP4RfF9

Upvotes: 0

Gho5t
Gho5t

Reputation: 1060

Your controller code is causing the error. You should be callling $scope.getAllUsers(); in your activate function, not "getAllUsers()".

Upvotes: 0

Related Questions