3

I never had to test my angularjs directives before, also the directives I wrote for my current company is uses events to communicated directives to directives and services.

And so I wrote a directive, e.g. a search directive.

<m-search />

This directive broadcasts "searchbox-valuechanged" event and the key, now I have to write tests for it.

'use strict';

describe('<m-search>', function() {

  beforeEach(module('hey.ui'));
  var rootScope;
  beforeEach(inject(function($injector) {
    rootScope = $injector.get('$rootScope');
    spyOn(rootScope, '$broadcast');
  }));

  it("should broadcast something", function() {
    expect(rootScope.$broadcast).toHaveBeenCalledWith('searchbox-valuechanged');
  });

});

Update On change on the input,

<input class="m-input m-input--no-border" type="search" placeholder="Search"
       ng-model="ctrl.searchValue"
       ng-model-options="{debounce: 100}"
       ng-change="ctrl.onChange({search: ctrl.searchValue})">

It calls a method in the directive's controller

vm.onChange = function (searchValue) {
  $rootScope.$broadcast('searchbox-valuechanged', {data: searchValue});
};

How do I test broadcasting?

Phil
  • 157,677
  • 23
  • 242
  • 245
Joey Hipolito
  • 3,108
  • 11
  • 44
  • 83
  • Where is the `searchbox-valuechanged` event handled? Outside the directive? – Michael Benford May 18 '15 at 03:37
  • yep, the `searchbox-valuechanged` is broadcasted using the $rootScope, so the `$rootScope` is the only dependency and directives are totally decoupled. – Joey Hipolito May 18 '15 at 03:40
  • What triggers the broadcast? You need to replicate that in your test code – Phil May 18 '15 at 03:44
  • Okay, I'll edit the question, btw, this triggers the broadcast. `'ng-change="ctrl.onChange({search: ctrl.searchValue})">' +` that is part of the template, and in the directive controller, it calls the ctrl.onChange – Joey Hipolito May 18 '15 at 03:46

1 Answers1

4

Here's how I'd do it...

describe('m-search directive', function() {
    var ctrl, // your directive's controller
        $rootScope; // a reference to $rootScope

    beforeEach(function() {
        // bootstrap your module
        module('hey.ui');

        inject(function($compile, _$rootScope_) {
            $rootScope = _$rootScope_;
            // see https://docs.angularjs.org/api/ngMock/function/angular.mock.inject#resolving-references-underscore-wrapping-

            // create an instance of your directive
            var element = $compile('<m-search></m-search')($rootScope.$new());
            $rootScope.$digest();

            // get your directive's controller
            ctrl = element.controller('mSearch');
            // see https://docs.angularjs.org/api/ng/function/angular.element#methods

            // spy on $broadcast
            spyOn($rootScope, '$broadcast').and.callThrough();
        });
    });

    it('broadcasts searchbox-valuechanged on change', function() {
        var searchValue = {search: 'search string'};
        ctrl.onChange(searchValue);

        expect($rootScope.$broadcast).toHaveBeenCalledWith(
            'searchbox-valuechanged', {data: searchValue});
    });
});

You'll note this doesn't rely on your directive's template at all. I don't believe template functionality is in the realm of unit testing; that's something best left to e2e testing with protractor. Unit testing is about testing the API of your components to ensure they do what they're meant to do.

Phil
  • 157,677
  • 23
  • 242
  • 245