MathKimRobin
MathKimRobin

Reputation: 1480

AngularJS test, can't find my controller

I met a problem with testing my controllers. I load them dynamically (lazy load, see after) so I wrote them like this :

angular.module('myApp').controllerProvider.register('DashboardCtrl', [
    '$scope', function ($scope) {
        $scope.foo = 'bar';
    }
]);

I initialise my module like this :

var app = angular.module('myApp', [
    'ngRoute',
    'ngCookies',
    'ngResource',
    'ngSanitize',
    'ui.router'
]);

app.run([
    '$rootScope', '$state', '$stateParams',
    function ($rootScope, $state, $stateParams) {
        $rootScope.$state = $state;
        $rootScope.$stateParams = $stateParams;
    }
]);

app.config(function ($stateProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
    app.stateProvider = $stateProvider;
    app.routeProvider = $urlRouterProvider;
    app.controllerProvider = $controllerProvider;
    app.compileProvider = $compileProvider;
    app.filterProvider = $filterProvider;
    app.provide = $provide;

    $urlRouterProvider.otherwise('/');

    $stateProvider
        .state('dashboard', {
            url         : '/',
            templateUrl : 'views/dashboard.html',
            controller  : 'DashboardCtrl',
            resolve     : {
                deps : function ($q, $rootScope) {
                    var deferred = $q.defer();

                    curl('scripts/controllers/dashboard.js')
                        .then(function () {
                            $rootScope.$apply(function () {
                                deferred.resolve({});
                            });
                        });

                    return deferred.promise;
                }
            }
        });
});

That works. Method is described there : http://ify.io/lazy-loading-in-angularjs/

But when I want to test, I get each time the error : Error: [ng:areq] Argument 'DashboardCtrl' is not a function, got undefined

My test script :

describe('Controller: DashboardCtrl', function () {
    'use strict';

    var DashboardCtrl,
        scope;

    // load the controller's module
    beforeEach(function () {
        var app = angular.module('cellierApp', [
            'ngRoute',
            'ngCookies',
            'ngResource',
            'ngSanitize',
            'ui.router'
        ]);

        app.run([
            '$rootScope', '$state', '$stateParams',
            function ($rootScope, $state, $stateParams) {
                $rootScope.$state = $state;
                $rootScope.$stateParams = $stateParams;
            }
        ]);

        app.config(function ($stateProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
            app.stateProvider = $stateProvider;
            app.routeProvider = $urlRouterProvider;
            app.controllerProvider = $controllerProvider;
            app.compileProvider = $compileProvider;
            app.filterProvider = $filterProvider;
            app.provide = $provide;
        });

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

    it('should attach a list of awesomeThings to the scope', function () {
        expect(scope.foo).toBeDefined();
        expect(scope.foo).toBe('bar');
    });
});

I don't know what I'm doing wrong. Is there someone who knows ? Thanks in advance

Upvotes: 0

Views: 3003

Answers (3)

Nelson Yeung
Nelson Yeung

Reputation: 3392

I'm not sure whether Karma has support for Script.js loading, but for sure it has support for Require.js.

Since you followed that tutorial, have you checked out the final example he gave on github (It contains unit testing as well)? The idea is that you can convert everything to use Require.js instead of Script.js and use Karma with karma-requirejs for the testing.

Although it is not too difficult, there are quite a lot of changes. I won't post the working code here, but I would suggest reading the following: http://ify.io/unit-testing-lazily-loaded-angularjs-artefacts/ and http://karma-runner.github.io/0.8/plus/RequireJS.html

I can't link the final example because I don't have the reputation, but it's at the bottom of the tutorial you've linked.

To see a runnable example using Asynchronous Module Definitions with RequireJS, have a look at the sample app."

Upvotes: 2

Nikola Yovchev
Nikola Yovchev

Reputation: 10256

You don't load modules, but override the angular modules in this snippet:

 var app = angular.module('cellierApp', [
    'ngRoute',
    'ngCookies',
    'ngResource',
    'ngSanitize',
    'ui.router'
  ]);

The proper call would be

beforeEach(function() {
    // the module to be tested
    module('cellierApp');
});

Also, the var declaration of the app is very crappy imo. You have angular as a container, use it, don't create objects on global closure for no reason. Change this:

var app = angular.module('myApp', [
    'ngRoute',
    'ngCookies',
    'ngResource',
    'ngSanitize',
    'ui.router'
]);

app.run...

To:

angular.module('myApp', [
        'ngRoute',
        'ngCookies',
        'ngResource',
        'ngSanitize',
        'ui.router'
    ])
.config([...

.run([....

The unit test itself can be simplified so much:

describe('Controller: DashboardCtrl', function() {
    'use strict';

    var scope;

    // load the controller's module
    beforeEach(function() {
        module('cellierApp');
    });

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

    it('should attach a list of awesomeThings to the scope', function() {
        expect(scope.foo).toBeDefined();
        expect(scope.foo).toBe('bar');
    });
});

Upvotes: 1

Mark-Sullivan
Mark-Sullivan

Reputation: 186

In your test you are not registering your controller

put your code for creating you controller in your test

app.controllerProvider.register('DashboardCtrl', [
    '$scope', function ($scope) {
        $scope.foo = 'bar';
    }
]);

however depending on your test setup your app and controller files should be loaded through your test system and you should use

beforeEach(module('cellierApp'));

instead of creating your app directly in your test

Upvotes: 0

Related Questions