A test for a controller follows similar a pattern to the one for a service. Let’s have a look at the fragment of the ProjectsEditCtrl controller from the sample application. This controller in question is responsible for the editing projects in the administration part of the application. Here we are going to test methods of the controller responsible for adding and removing project’s team members:

angular.module('admin-projects', [])
  .controller('ProjectsEditCtrl', function($scope, project) {

    $scope.project = project;

    $scope.removeTeamMember = function(teamMember) {
      var idx = $scope.project.teamMembers.indexOf(teamMember);
      if(idx >= 0) {
        $scope.project.teamMembers.splice(idx, 1);
      }
    };

    //other methods of the controller
  });

The logic of the presented controllers is not complex and will let us to focus on the test itself:

describe('ProjectsEditCtrl tests', function () {

  var $scope;
  beforeEach(module('admin-projects'));
  beforeEach(inject(function ($rootScope) {
    $scope = $rootScope.$new();
  }));

  it('should remove an existing team member', inject(function ($controller) {

    var teamMember = {};
    $controller('ProjectsEditCtrl', {
      $scope: $scope,
      project: {
        teamMembers: [teamMember]
      }
    });

    //verify the initial setup
    expect($scope.project.teamMembers).toEqual([teamMember]);

    //execute and verify results
    $scope.removeTeamMember(teamMember);
    expect($scope.project.teamMembers).toEqual([]);
  }));
});

The removeTeamMember method that we want to test here will be defined on a $scope and it is the ProjectsEditCtrl controller that defines this method. To effectively test the removeTeamMember method we need to create a new scope, a new instance of the ProjectsEditCtrl controller and link the two together. Essentially we need to manually do the job of the ng-controller directive.

Let’s turn our attention to the beforeEach section for one more moment, as there are interesting things going on in there. Firstly we are getting access to the $rootScope service and creating a new $scope instance ($scope.$new()). We do so to simulate what would happen in a running application, where the ng-controller directive would create a new scope.

To create an instance of the controller we can use the $controller service (please notice how the inject function can be placed on the beforeEach section as well as on the it level).

Note

Look how easy it is to specify controller’s constructor arguments while invoking the $controller service. This is dependency injection at its best; we can provide both a fake scope and test data to exercise controller’s implementation in complete isolation.

Advertisements