Healforgreen
Healforgreen

Reputation: 579

Angular unit testing: Argument 'fn' is not a function, got Object

This error is occurring when I try to instantiate the setup phase of my unit test. I am unit testing a directive, which has its own controller. For best practice purposes I can always add the controllerAs property to the directive to assign the controller a name, but I get the same error if I do that anyway.

describe('myDirective', function() {        

    beforeEach(module('app'));
    beforeEach(module('app/directives/directive.html'));

    var theArgs = { 
        arg1 : [],
        arg2 : 'id',
        arg3 : [],
        arg4 : '',
        arg5 : false
    };  

    beforeEach(inject(function($templateCache, _$compile_, _$rootScope_, $controller) {
        template = $templateCache.get('app/directives/directive.html');
        $templateCache.put('app/directives/directive.html', template);
        $compile = _$compile_;
        $rootScope = _$rootScope_;
        scope = $rootScope.$new();
        scope.args = theArgs;
        ctrl = $controller({
            $scope: scope
        });
    }));

    it('should compile', function() {
        var myElement = angular.element('<div my-directive args="theArgs" ></div>');
        var element = $compile(myElement)(scope);

        // Grab scope. Depends on type of scope.
        scope = element.isolateScope() || element.scope();
        // Grab controller instance
        controller = element.controller(ctrl);
        $rootScope.$digest();        

        // Mock the directive's controller's add() function
        scope.add();                
     });    
});

The error is occurring within this block:

ctrl = $controller({
    $scope: scope
});

Since the controller doesn't have a name, I am not passing it one in the above code block. That shouldn't throw an error by itself though, right? I don't think there is a problem with my karma configuration since my other 500 tests are all passing.

The second error is being thrown at controller = element.controller(ctrl);, where it can't find the ctrl variable. That error makes sense because it's caused by the first error, but I can't figure out how to fix the first error.

UPDATE: Added directive code to show how the controller was defined. It was never assigned a name, it is anonymous, and I didn't use the controllerAs property, because it returns an error.

app.directive('myDirective', function() {
    var dirController = ['$scope', function($scope) {
        $scope.add = function() { ... };
    }];

    return {
        restrict: 'A',
        scope: {
            args: '='
        },
        templateUrl: '/path/to/template.html',
        controller: dirController
    };
});

Upvotes: 3

Views: 1021

Answers (1)

Iamisti
Iamisti

Reputation: 1710

Well the problem is exactly with this code section:

ctrl = $controller({
    $scope: scope
});

Since $controller is require the name of the controller for the first parameter, and then the injectables afterwards within an object literal. E.g.: Tell it which controller should it create:

ctrl = $controller('MyControllerName', {
    $scope: scope
});

Upvotes: 1

Related Questions