Check out myAngular article series with live demos and Facebook group Angular - Advanced Topics

Unit Testing bindToController and ControllerAs

Torgeir "Tor" Helgevold
- JavaScript Developer and Blogger
Published: Sat Apr 04 2015

The bindToController property makes it really easy to tie a directive's isolate scope to the controller when using controllerAs. BindToConroller effectively binds the scope properties to the controller, but how can we simulate this in a unit test without having to include the directive in the test? In this post I will show an example of how to do it.

The code listing below shows a simple greeting controller that produces a greeting based on two isolate scope properties set via the markup

Controller and Directive

angular.module('greeting-project').controller('greetingController', function(){ this.greeting = 'Hello, ' + this.fn + ' ' + this.ln; } ); angular.module('greeting-project').directive('greetingDirective', function(){ return{ scope:{fn:'@',ln:'@'}, restrict:'E', template:'<div>{{greetingController.greeting}}</div>', controller:'greetingController', controllerAs:'greetingController', bindToController:true }; } );

Html

<greeting-directive fn="Joe" ln="Smith"></greeting-directive>

As you can tell, the greeting depends on fn and ln and needs them right when the controller is instantiated. Previously this created a challenge for mocking since there was no official hook to easily pass the value on behalf of the directive. Luckily for us, the Angular team has now added support for mocking this when instantiating the controller in unit tests.

The following code listing show how to pass an object to $controller(..) to supply the needed values in a Jasmine unit test:

describe('greetingController', function() { var ctrl; beforeEach(module('greeting-project')); beforeEach(inject(function(_$rootScope_, _$controller_){ //Simulating isolate scope variables from the directive var data = { fn:'Joe', ln:'Smith' }; var scope = _$rootScope_.$new(); //scope not needed, but just showing how to pass standard dependencies as well as custom data dictionary //the second argument is an object of locals - all injected dependencies //the third argument is the data dictionary for simulating isolate scope variables ctrl = _$controller_('greetingController', {$scope: scope}, data); })); it('should create greeting', function() { expect(ctrl.greeting).toBe('Hello, Joe Smith'); }); });

I was really happy to see this feature added since it greatly simplifies unit testing and allows us to test controllerAs controllers in isolation from the directive.

If you liked this article, share it with your friends on social media:

We also have a new Facebook group about advanced Angular topics.

I invite you to follow me on twitter