toadjamb
toadjamb

Reputation: 934

How to test AngularJS directives

I am working on a Rails 3.2 app that will be using AngularJS. I can get Angular to do what I need, but I am having a very difficult time figuring out how to test what I'm doing. I am using guard-jasmine to run Jasmine specs using PhantomJS.

Here is the (relevant) html:

<html id="ng-app" ng-app="app">
  <div id="directive-element" class="directive-element">
  </div>
</html>

The javascript (in coffeescript) looks like:

window.Project =
  App: angular.module('app', [])
  Directive: {}

Project.Directive.DirectiveElement =
  ->
    restrict: 'C'
    link: (scope, element, attrs) ->
      element.html 'hello world'
Project.App.directive 'directiveElement', Project.Directive.DirectiveElement

The code above does exactly what it is intended to do. The tests are the problem. I can't get them to work at all. This is one thing I had tried. Posting this is mostly just to start the conversation somewhere.

describe 'App.Directive.DirectiveElement', ->
  it 'updates directive-element', ->
    inject ($compile, $rootScope) ->
      element = $compile('<div id="app" ng-app="app"><div id="directive'element" class="directive-element"></div></div>')
      expect(element.text()).toEqual('hello world')

As an aside, I am new to AngularJS, so if there are any best practices regarding namespacing, modules, etc. that I am not following, guidance would be appreciated.

How do I get a test for this to work?

Upvotes: 45

Views: 26931

Answers (2)

SavoryBytes
SavoryBytes

Reputation: 36246

Angular Test Patterns may help you, there are examples in both coffeescript and javascript.

Here's a testing pattern to verify the example directive is rendering the expected output.

Upvotes: 12

Andrew Joslin
Andrew Joslin

Reputation: 43023

Here's how alert directive is tested in angular-ui/bootstrap.

Here's another simple set of tests, for the buttons directive.

Here are a few tips:

  • Be sure to tell the test runner what module you are testing with beforeEach(module('myModule')).

  • If you have external templateUrls in your directives, you'll want to somehow pre-cache them for the test runner. The test runner can't asynchronously GET templates. In bootstrap, we inject the templates into the javascript with a build step, and make each template a module. We use grunt-html2js grunt task.

  • In your tests, use the inject helper in a beforeEach to inject $compile and $rootScope and any other services you'll need. Use var myScope = $rootScope.$new() to create a fresh scope for each test. You can do var myElement = $compile('<my-directive></my-directive>')(myScope); to create an instance of your directive, and have access to its element.

  • If a directive creates its own scope and you want to test against it, you can get access to that directive's scope by doing var directiveScope = myElement.children().scope() - It will get the element's child (the directive itself), and get the scope for that.

  • For testing timeouts, you can use $timeout.flush() to end all pending timeouts.

  • For testing promises, remember that when you resolve a promise, it will not call its then callbacks until the next digest. So in tests you have to do this a lot: deferred.resolve(); scope.$apply();.

You can find tests for directives of varying complexity in the bootstrap repo. Just look in src/{directiveName}/test/.

Upvotes: 69

Related Questions