Victor Queiroz

AngularJS, Jasmine, Karma: Testing Directives

If you haven’t yet experienced using ngMock with Jasmine, you don’t know what you’re missing — it’s all wonderful and powerful. Keep reading, let’s go!

As I mentioned at the end of the article AngularJS: Creating Tests with Jasmine and Karma, you can use Angular’s $injector to call everything you need to work with your modules from anywhere. In this guide we’ll use the following dependencies:

  • $injector
  • $rootScope
  • $compile

Importing the dependencies

Let’s start by calling our dependencies inside our test:

describe('app module', function () {
    var $compile, $rootScope;

    beforeEach(module('app'));

    beforeEach(inject(function ($injector) {
        $compile = $injector.get('$compile');
        $rootScope = $injector.get('$rootScope');
    }));
});

Above, we’ve imported the dependencies that will be reused by the upcoming suites. Now let’s import the dependencies for our directive:

describe('app module', function () {
    var $compile, $rootScope;

    beforeEach(module('app'));

    beforeEach(inject(function ($injector) {
        $compile = $injector.get('$compile');
        $rootScope = $injector.get('$rootScope');
    }));

    describe('replacer directive', function () {
        var element, scope, form;

        beforeEach(inject(function ($injector) {
            scope = $rootScope.$new();
        }));
    });
});

Now we need to compile our element inside the replacer directive suite. We’ll do it like this:

describe('replacer directive', function () {
    var element, scope, form;

    beforeEach(inject(function ($injector) {
        scope = $rootScope.$new();
        element = '<form>' + '<input ng-model="text" name="text" replacer-directive>' + '</form>';
        element = angular.element(element);
        element = $compile(element)(scope);
    }));
});

Now you might be wondering:

How on earth am I going to get my input? Do I have to do gymnastics with jQuery or document.querySelector?

Answer: NO! Just assign a value to the only variable we haven’t defined yet inside the replacer directive suite:

beforeEach(inject(function ($injector) {
    scope = $rootScope.$new();
    element = '<form>' + '<input ng-model="text" name="text" replacer-directive>' + '</form>';
    element = angular.element(element);
    element = $compile(element)(scope);
    form = scope.form;
    scope.$digest();
}));

Now we can access our input through the form variable, like this:

form.text

Note that we used a new method on the second-to-last line: scope.$digest(). It takes care of updating the scope — any modifications you make to the scope should be followed by a scope.$digest() so that all changes are applied to the DOM.

Now we can write our tests:

it('should replace 2 with 3', function () {
  scope.text = 252;
  scope.$digest();

  expect(form.text).toBe(352);
});

From here on, we can live happily ever after and test our directives in every possible way to make sure everything works exactly as expected.

As always, leave your questions in the comments. Cheers everyone and until next time!

Comments