Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test controller directives in AngularJS

After many research, I've been unable to correctly test an Angular directive, since I can't access to functions inside its controller.

Here is the directive code:

angular.module('app').
    directive("accordionItem", function () {
        return{
            restrict: 'E',
            replace: true,
            templateUrl: function (elem, attr) {
                return 'partials/invoice/' + attr.temp + '.html';
            },
            scope: {
                invoice: '=',
                temp: '@'
            },
            controller: function ($scope, listSelectionService, $state) {

            $scope.selectItem = function () {
                if ($scope.isOpen()) {
                    listSelectionService.selectedItem = -1;
                }
                else {
                    listSelectionService.selectedItem = $scope.invoice;
                }
            };
            $scope.isOpen = function () {
                return listSelectionService.selectedItem === $scope.invoice;
            };
            $scope.showFaturasSubscription = function () {
                $state.go('app.ultimasFaturasSubscription', {subscriptionId: $scope.invoice.subscriptionId});
            };
        }
    };
});

And my test:

describe('Invoice', function() {
    var $scope = {}, controller, $httpBackend, $controller, form, element;
    beforeEach(module('app'));

    describe('Directives', function() {
        beforeEach(inject(function($compile, $rootScope, _$httpBackend_, _$controller_) {
            $httpBackend = _$httpBackend_;
            $httpBackend.expect('GET', 'data/translation/?lang=pt').respond(200, []);
            $httpBackend.when('GET', 'partials/invoice/undefined.html').respond(200, []);
            $httpBackend.when('GET', 'partials/templates/loading.html').respond(200, []);
            $httpBackend.when('GET', 'partials/invoice/invoiceContent.html').respond(200, []);
            $scope = $rootScope.$new();
            $controller = _$controller_;

            form = $compile("<accordion-item temp='invoiceContent'></accordion-item>")($scope);
            $scope.$digest();

        }));
        it('should submitButtonDisabled', inject(function($injector) {
            var listSelectionService = $injector.get("listSelectionService");
            $scope.selectItem();
            expect(listSelectionService.selectedItem).toBe(-1);
        }));
    });
});

According to many documents I've read, after the $digest() function we can have access to the controller of the directive. This is not happening since it gives me the following error:

TypeError: $scope.selectItem is not a function
at null.<anonymous> (http://localhost:8234/spec/invoice/invoiceDirectivesSpec.js:27:20)
at Object.invoke (http://localhost:8234/src/main/webapp/vendor/angular/angular.js:4452:17)
at workFn (http://localhost:8234/src/main/webapp/vendor/angular-mocks/angular-mocks.js:2420:20)
at jasmine.Block.execute (http://localhost:8234/?:1164:19)
at jasmine.Queue.next_ (http://localhost:8234/?:2196:33)
at jasmine.Queue.start (http://localhost:8234/?:2149:10)
at jasmine.Spec.execute (http://localhost:8234/?:2476:16)
at jasmine.Queue.next_ (http://localhost:8234/?:2196:33)
at jasmine.Queue.start (http://localhost:8234/?:2149:10)
at jasmine.Suite.execute (http://localhost:8234/?:2621:16)
Error: Declaration Location
    at window.inject.angular.mock.inject (http://localhost:8234/src/main/webapp/vendor/angular-mocks/angular-mocks.js:2391:25)
    at null.<anonymous> (http://localhost:8234/spec/invoice/invoiceDirectivesSpec.js:25:43)
    at jasmine.Env.describe (http://localhost:8234/?:919:23)
    at describe (http://localhost:8234/?:703:29)
    at null.<anonymous> (http://localhost:8234/spec/invoice/invoiceDirectivesSpec.js:10:5)
    at jasmine.Env.describe (http://localhost:8234/?:919:23)
    at describe (http://localhost:8234/?:703:29)
    at http://localhost:8234/spec/invoice/invoiceDirectivesSpec.js:5:1

Any help would be really appreciated.

like image 348
Andre Sanguinetti Avatar asked Oct 02 '15 15:10

Andre Sanguinetti


People also ask

What is testing directives in AngularJS?

Testing Directives. Directives are the most important and most complex components in AngularJS. Testing directives is tricky, as they are not called like a function. In applications, the directives are declaratively applied on the HTML template. Their actions are executed when the template is compiled and a user interacts with the directive.

What is the use of controller in AngularJS?

Using Controllers In Directives In AngularJS. In AngularJS, you have your Views, which present data to the user; you have your Controllers, which manage the $scope (ie. view model) and expose behavior to the View; and, you have your Directives, which link user interactions to $scope behaviors.

What are the best testing tips for AngularJS?

AngularJS Testing Tips: Testing Directives. Unit tests are an essential part of software development as they help you in releasing less buggy code. Testing is one of the several things that one has to do to improve code quality. AngularJS is created with testing in mind and any code written on top of the framework can be tested easily.

What is the difference between a directive and an ngcontroller?

When a Directive requires a Controller, it is only requiring a "directive controller". The directive doesn't have any access to the ngController-based controllers. The only thing that a directive and an ngController has is that they share (or at least share by default) the same $scope reference.


1 Answers

I usually test directive controllers the same way I do a regular controller.

In your directive, you have defined the controller inline, as part of the directive. Instead, define it like you would for a controller that is used with a view:

Register the controller on a module:

angular.module('app').controller('DirectiveController', function($scope) { ... });

Reference the controller in the directive configuration:

controller: 'DirectiveController'

Test the controller:

This would either replace or complement the actual directive tests. Testing the controller outside of the directive is much simpler, you don't need to worry about instantiating the directive or deal with DOM elements. Often times, if the template for the directive is simple enough, I don't even bother with directive tests and just test the controller. Simple example:

var controller, scope;

beforeEach(inject(function($rootScope, $controller) {
    scope = $rootScope.$new();
    controller = $controller('DirectiveController', {$scope: scope});
}));

describe('controller', function() {
  it('exists', function() {
    expect(controller).toBeDefined();
    expect(controller).not.toBeNull();
  });
});
like image 183
Sunil D. Avatar answered Sep 20 '22 17:09

Sunil D.