Unit testing a directive that 'requires' a controller from another directive

4,252 views
Skip to first unread message

Stuart Plumbley

unread,
Jul 8, 2013, 4:40:30 AM7/8/13
to ang...@googlegroups.com
Hi All,

I'm not sure where to start trying to test a directive that requires a controller from another directive. Do you have to unit test the two together? That doesn't feel geat if that is the case.

I have got quite a bit of experience so far testing directives, but I'm really scratching my head where to start with this one.

Sorry I don't have an example to talk through, but just the general concepts of where to start testing this kind of structure would be appreciated.


Peter Bacon Darwin

unread,
Jul 8, 2013, 7:08:57 AM7/8/13
to ang...@googlegroups.com
I guess you are thinking it would be nice to mock out the required directive controller?
If one directive requires the other then they are pretty tightly coupled (at least in one direction).  It is probably easiest to actually provide it in your tests.
You can do this by including the required directive in the HTML that you compile in the test.  This is common practice when testing validator directives:

var element = $compile('<input ng-model="obj.val" my-validator>')($rootScope);

Here the myValidator directive required ngModel and so we simply provide it.  There is not much benefit in mocking this out.

A slightly more complex case might be where you have an optional ancestral requirement.  Again it seems much simpler to just provide this but it is a little more difficult to get access to your directive's element:

var container = $compile('<div parent-required-directive><div id="subject" directive-under-test></div></div>')($rootScope);
var element = container.children()[0];

If you really wanted to mock it out, you could manually attach the controller to an element, with something like this (UNTESTED):

var element = angular.element('<input my-validator>');
var locals = {
              $scope: $rootScope,
              $element: element,
              $attrs: { $attrs: {} },
              $transclude: angular.noop
            }
$element.data('$ngModelController', $controller(myMockNgModelController, locals));
$compile(element)($rootScope);





--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Julien B

unread,
Dec 3, 2013, 8:45:36 PM12/3/13
to ang...@googlegroups.com
Hi,

I have a directive with isolate scope that require ngModel and make calls to the ngModelController.$setValidity.

I'd like to spy these calls in my directive unit tests, but didn't find any working example yet.

How should this be implemented ? Cant make the above example work :/

Thanks

Julien

Daniel Tabuenca

unread,
Dec 4, 2013, 1:18:35 AM12/4/13
to ang...@googlegroups.com
You can inject the directive into the controller and alter it's link and controller functions before calling $compile. Here is an example:

Julien B

unread,
Dec 4, 2013, 4:21:55 AM12/4/13
to ang...@googlegroups.com
Wow really nice tip, thanks a ton Daniel, you made my day !

Julien B

unread,
Dec 4, 2013, 7:03:07 PM12/4/13
to ang...@googlegroups.com
the only issue i get now is that the ngModelController is broken if we do like this because all the methods are discarded. should i have to rewrite all the ngModelController methods i if i just want a spy on $setValidity ?

thanks

Daniel Tabuenca

unread,
Dec 4, 2013, 8:55:01 PM12/4/13
to ang...@googlegroups.com

It’s a bit more tricky. But you can do it like this:

 var ngModel = ngModelDirective[0];

    //Save old controller  it's actually at index 5 due to injection array syntax
    var oldController = ngModel.controller[5];

    //Override controller
    ngModel.controller = function($scope, $exceptionHandler, $attrs, $element, $parse){


       //Let the old controller do it's thing
       oldController.apply(this, arguments);

       this.$setValidity = jasmine.createSpy();

       //Save the controller to the test variable
       ngModelController = this;
    };

http://plnkr.co/edit/B37j6Xjl0HmFjVKAGJq0?p=preview

I’m not sure if this is the best or recommended way of doing it, but it works. Can anyone think of an easier way?

Julien B

unread,
Dec 5, 2013, 7:30:52 PM12/5/13
to ang...@googlegroups.com
Thanks Daniel,

I ended up with something simpler : wrapping my directive inside a ngForm so i can easily test validity here with scope.form.$valid

thanks !

Daniel Tabuenca

unread,
Dec 6, 2013, 1:24:08 AM12/6/13
to ang...@googlegroups.com
That's probably a better idea. Directive tests tend to be more on the integration testing side of the spectrum.
Reply all
Reply to author
Forward
0 new messages