Mike R
Mike R

Reputation: 961

How to correctly accessing the scope and controller of a directive in a Jasmine unit test

The gist of my issue is that I don't think I am correctly accessing the scope and controller of my directive in accompanying unit tests.

I have a directive that looks something like this:

app.controller('GalleryDirectiveController', ['$scope', '$element', '$timeout', function($scope, $element, $timeout){
    $scope.panels = [];

    this.addPanel = function(panel){
        // Timeout to help with $scope.$watch in other directives
        return $timeout(function(){ $scope.panels.push(panel); }, 0);
    };
}]);

app.directive('gallery', function(){
    return {
        restrict: 'E',
        controller: 'GalleryDirectiveController'
    };
});

What I am trying to do now is write unit tests that mock a collection of gallery panels that add themselves to the gallery. Unfortunately, I don't think I am correctly accessing the controller and scope of the directives in my unit tests, so they never pass.

The unit test looks something like this:

describe('app Gallery', function(){
    var gallery;
    var $scope;

    beforeEach(module('app'));

    beforeEach(inject(function($compile, $rootScope){

        var element = angular.element('<gallery/>');
        $compile(element)($rootScope.$new());
        $rootScope.$digest();

        $scope = element.isolateScope() || element.scope();
        gallery = element.controller('gallery');

    }));

    it('should add panels to the gallery', function(){
        for (var i = 0; i < 9; i++) {
            gallery.addPanel({
                $el : $('<div></div>')
            });
        }

        // Each panel is added in a timeout, so I thought
        // the problem might have been a timing issue
        waitsFor(function(){
            return $scope.panels.length !== 0;
        }, 'the panels to be added', 200);

    });
});

$scope.panels.length is always zero. This makes me think that the $scope variable I am setting in my unit test is not the same $scope being modified by the controller. For what it's worth, other unit tests that check if gallery.addPanel and $scope.panels are defined pass as expected.

Hopefully I am just missing something small. My fear is that I may have created a hard-to-test gallery directive.

Upvotes: 0

Views: 378

Answers (1)

PSL
PSL

Reputation: 123739

You can flush the $timeout before setting your expectation.

i.e:-

var $timeout; //or var flushTimeout;
...
beforeEach(inject(function($compile, $rootScope, _$timeout_){ //<-- inject timeout
        $timeout = _$timeout_; //Save it in outer scope
        //or flushTimeout = _$timeout_.flush


...

and in your test do:-

it('should add panels to the gallery', function(){
    for (var i = 0; i < 9; i++) {
        gallery.addPanel({
            $el : $('<div></div>')
        });
    }
    $timeout.flush(); //or flushTimeout();
    expect($scope.panels.length).toEqual(9);
});

Test Demo

Upvotes: 1

Related Questions