Unit Testing Angular Directives with Isolate Scope

Last reviewed on February 12, 2015

If you are building complex user interfaces, you will at some point want to start unit testing your directives. Below is a simple directive called blog-status that displays either "Active" or "Inactive" on the page. This might be a directive used in the admin section for a blog where it shows a list of all your posts and the status of each post. It's not the most useful directive but it is simple enough to show how to unit test a directive with isolate scope. Usage of this directive would be:

<blog-status is-active="blogArticle.active"></blog-status>

blogArticle.active would be a boolean value. And here is the directive implementation:

app.directive('blogStatus', function () {
  return {
    restrict: 'E',
    template: '<span>{{activeDisplayName}}</span>',
    scope: {
      isActive: '=',
    },
    link: function ($scope, $el, attrs) {
      if ($scope.isActive) {
        $scope.activeDisplayName = 'Active';
      } else {
        $scope.activeDisplayName = 'Inactive';
      }
    },
  };
});

This directive uses an isolate scope where isActive is passed in as blogArticle.active from the is-active attribute. So the first thing you might ask yourself is, what should I unit test here? What I would want to test is ensuring that the property activeDisplayName on the isolate scope gets set to "Active" if blogArticle.active is true and "Inactive" when false. Let's write a unit test for that:

describe('active directive', function () {
  var scope, $compile;

  beforeEach(module('blog'));
  beforeEach(inject(function ($rootScope, _$compile_) {
    scope = $rootScope.$new();
    $compile = _$compile_;
  }));

  it('should display "Active" if passed true', function () {
    var element = '<blog-status is-active="blogArticle.active"></blog-status>';
    scope.blogArticle = { active: true };
    element = $compile(element)(scope);
    scope.$digest();

    expect(element.isolateScope().activeDisplayName).toEqual('Active');
  });
});

Looking at the test above, one of the first things you'll notice is the $compile service. If you've used another client-side templating library before like Handlebars, this is very similar to Handlebars.compile(htmlString). This basically compiles an HTML string into an optimized template function which can be used later on to interpolate the template with data from $scope.

The isolate scope is the thing we want to test. To access it, we can call element.isolateScope() and access the properties on that scope.

To test this directive when blogArticle.active is false we can do the opposite:

it('should display "Inactive" if passed false', function () {
  var element = '<blog-status is-active="blogArticle.active"></blog-status>';
  scope.blogArticle = { active: false };
  element = $compile(element)(scope);
  scope.$digest();

  expect(element.isolateScope().activeDisplayName).toEqual('Inactive');
});