This is me

Hi! I'm Dag-Inge.

This blog post is a follow up to the popular Testing AngularJS directive templates with Jasmine and Karma blog post I did earlier.

As with that post, we are going to focus on testing directives in AngularJS. This time, we will focus on testing controllers, which can pose a bit of a challenge because everyone writes directives like the one below, with an anonymous function for the controller.

 1 angular.module('myApp').directive('test', function() {
 2     return  {
 3         templateUrl: '/some/template/url.html',
 4         restrict: 'E',
 5         replace: true,
 6         controller: function ($scope) {
 7             $scope.open = false;
 8             $scope.toggle = function () {
 9                 $scope.open = !$scope.open;
10             };
11         }
12    };
13 });

Now, the problem is that the usual approach to controller testing is to instantiate the controller using $controller, but this fails since we do not have a named controller registered on our module.

However, there is a solution. First of all, you could simply register a controller on your module, and then use that as you normally would. Or, if you have, like me, written code like the one above for a lot of directives without really testing them before, there is another approach that doesn’t leave you refactoring.

By compiling the element like we did in directive template testing, we can access the scope and start testing the publicly accessible methods right away.

1 $scope = $rootScope.$new();
2 var element = angular.element("<test></test>");
3 template = $compile(element)($scope);
4 $scope.$digest();

You now have access to the $scope, and can test that as you usually would. Below is a minimal example testing the toggle() method above with Jasmine.

 1 describe("Directive", function () {
 2 
 3     var $scope;
 4 
 5     beforeEach(inject(function($rootScope, $compile) {
 6         $scope = $rootScope.$new();
 7         var element = angular.element("<test></test>");
 8         template = $compile(element)($scope);
 9         $scope.$digest();
10         controller = element.controller;
11     }));
12 
13     it("should toogle open when toggle() is called", inject(function() {
14         $scope.open = false;
15         $scope.toggle();
16         expect($scope.open).toBeTruthy();
17         $scope.toggle();
18         expect($scope.open).toBeFalsy();
19     }));
20 
21 });

So with that, you can now access everything on the $scope and start testing!