Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Controller Instantiation In Angular Directive Unit Test

I am unit testing an Angular directive and would like to mock or stub in some way the instantiation of the named controller in the unit test.

So first I suppose on to some code...

'use strict';  angular.module('App.Directives.BreadCrumbs', [])      .directive('kxBreadcrumbs', function () {         return {             restrict: 'E',             controller: 'BreadCrumbsController',             template:                 '<!-- Breadcrumbs Directive HTML -->' +                  '<ol class="breadcrumb">' +                 '    <li ng-repeat="crumb in crumbPath">' +                 '       <a ng-class="{true: \'disable\', false: \'\'}[crumb.last]" href="{{crumb.href}}" ng-click="updateCrumb(crumb.name)">{{crumb.name}}</a>' +                 '    </li>' +                 '</ol>' +                  '<!-- End of Breadcrumbs Driective HTML -->'         };      }); 

This is one sample directive that I would unit test, the important thing to take away from this is the named controller.

So in my unit test

'use strict';  describe('Directives: Breadcrumbs', function () {      var//iable declarations         elm,         scope,         $rootScope     ;      beforeEach(function () {         module('App.Directives.BreadCrumbs');         module('App.Controllers.BreadCrumbs');         module('App.Constants'); // <--- Comes from the controller dependancy     });       beforeEach(inject(function (_$rootScope_, $compile) {         $rootScope = _$rootScope_;         scope = $rootScope.$new();          elm = angular.element('<kx-breadcrumbs></kx-breadcrumbs>');         $compile(elm)(scope);         scope.$apply();     }));      it('Should create the breadcrumbs template', function () {         scope.crumbPath = [{name: 'home', href: '/'},{name: 'coffee', href: '/coffee'},{name: 'milk', href: '/milk'}];         scope.$apply();         var listItem = $(elm).find('li');         expect(listItem.length).toBe(3);         expect($(listItem).text()).toContain('home');         expect($(listItem).text()).toContain('coffee');         expect($(listItem).text()).toContain('milk');      }); }); 

You can see the inclusion of the 3 modules - the directive, the controller and the third one the constants. This is referenced by the controller as a dependancy so in order to pull this into the unit test I need to pull in the dependancy or in much worse cases the dependancies from the controller. But as I am not unit testing the functionality of the controller in the directive unit test, this seem redundant and bloating of code through inclusion of modules. Ideally I would like to only include the module that I am unit testing.

  module('App.Directives.BreadCrumbs'); 

and not (modules added to exemplify my point more)

  module('App.Directives.BreadCrumbs');   module('App.Controllers.BreadCrumbs');   module('App.Constants'); // <--- Comes from the controller dependancy   module('App.Service.SomeService'); // <--- Comes from the controller dependancy   module('App.Service.SomeOtherService'); // <--- Comes from the SomeService dependancy 

When we unit test controllers we can mock services that are passed in either completely or by using jasmine spies. Can we accomplish the same sorta thing in unit test of directives so I don't have to follow the dependancy trail?

like image 685
Sten Muchow Avatar asked Apr 12 '14 11:04

Sten Muchow


People also ask

What is mocking in unit testing Angular?

Introduction. Mocking is a great idea for testing Angular apps because it makes maintenance easier and helps reduce future bugs. There are a few complex tools, such as XUnit, for mocking an Angular CLI project. You can execute the mocking methods described in this guide only if you use vanilla Jasmine + Angular Testbed ...

Should unit test mock dependencies?

Correct. You should mock things that depend on anything persistent or external in order to prevent the test from depending on anything persistent or external.

Why mocking is necessary in unit testing?

What is mocking? Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.

Is AngularJS code unit testable?

AngularJS is written with testability in mind, but it still requires that you do the right thing. We tried to make the right thing easy, but if you ignore these guidelines you may end up with an untestable application.


1 Answers

You can create mocks in module configuration block by using $controllerProvider.register() for controllers, $provide.provider(), $provide.factory(), $provide.service() and $provide.value() for providers, factories and services:

JavaScript

beforeEach(function () {     module('App.Directives.BreadCrumbs', function($provide, $controllerProvider) {         $controllerProvider.register('BreadCrumbsController', function($scope) {             // Controller Mock         });         $provide.factory('someService', function() {             // Service/Factory Mock             return {                 doSomething: function() {}             }         });     }); }); 

Once you do so, Angular will inject your mock BreadCrumbsController controller into kxBreadcrumbs directive. This way you don't need to include real controller and it's dependencies into unit test.

For more information see Angular's official documentation on:

  • $provide
  • $controllerProvider
like image 85
Vadim Avatar answered Sep 17 '22 18:09

Vadim