Directives with isolate scope do not inherit their scope from their parent. Thus, the pattern to test them is slightly different.

When testing a directive, you’ll often want to setup some fixture element to attach your directive to. This will act as the DOM that you will compile with scope. At that point, the variables that you attached to scope will be available within the directive under test.

For example, if I had a directive called AccountList with isolate scope:

angular.module('app').directive 'AccountList', ->
  restrict: 'EA'
  replace: true
    accounts: '='
  # etc ...

Then I would setup my test something like this:

describe 'AccountList', ->

  elm = null

  beforeEach inject ($rootScope, $compile) ->
    accounts = []
    for num in [1..5]
      accounts.push createFakeAccount()
    $rootScope.accounts = accounts

    elm = angular.element """
      <account-list accounts="accounts"></account-list>

    e = $compile(elm)($rootScope)

  # actual specs, using elm ...

A few points:

  • inject() comes from angular-mock. It is currently only supported in Jasmine.
  • Before each test of the AccountList directive, a set of 5 accounts will be put in scope so the directive, presumably, has something to display.
  • $rootScope is going to be the only scope that you can get to easily from your tests. You can attach variables to it directly as above or call $rootScope.$new() to generate a new scope if you’d like.
  • We initially $compile the fixture element, passing in $rootScope.
  • To get Angular to actually act internally on the variables that we’ve passed into the directive and put them on scope, we must call the important line of code: e.scope().$digest().

Now you should be ready to rock and roll with your tests. Isolate scopes can be tricky. Testing them, a bit tricky as well.

Do you know a better way?