Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I am just starting with angular and I wanted to write some simple unit tests for my controllers, here is what I got.

app.js:

'use strict';


// Declare app level module which depends on filters, and services
angular.module('Prototype', ['setsAndCollectionsService']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/dashboard', {templateUrl: 'partials/dashboard.html', controller: 'DashboardController'});
    $routeProvider.when('/setsAndCollections', {templateUrl: 'partials/setsAndCollections.html', controller: SetsAndCollectionsController});
    $routeProvider.when('/repetition', {templateUrl: 'partials/repetition.html', controller: RepetitionController});
    $routeProvider.otherwise({redirectTo: '/dashboard'});
  }]);

and controllers.js

'use strict';

/* Controllers */

    var myApp = angular.module('Prototype');

    myApp.controller('DashboardController', ['$scope', function (scope) {
        scope.repeats = 6;
    }]);

/*function DashboardController($scope) {
 $scope.repeats = 5;
 };*/

function SetsAndCollectionsController($scope, $location, collectionsService, repetitionService) {
    $scope.id = 3;
    $scope.collections = collectionsService.getCollections();
    $scope.selectedCollection;
    $scope.repetitionService = repetitionService;

    $scope.switchCollection = function (collection) {
        $scope.selectedCollection = collection;
    };

    $scope.addCollection = function () {
        $scope.collections.push({
            name: "collection" + $scope.id,
            sets: []
        });
        ++($scope.id);
    };

    $scope.addSet = function () {
        $scope.selectedCollection.sets.push({
            name: "set" + $scope.id,
            questions: []
        });
        ++($scope.id);
    };

    $scope.modifyRepetition = function (set) {
        if (set.isSelected) {
            $scope.repetitionService.removeSet(set);
        } else {
            $scope.repetitionService.addSet(set);
        }

        set.isSelected = !set.isSelected;
    };

    $scope.selectAllSets = function () {
        var selectedCollectionSets = $scope.selectedCollection.sets;

        for (var set in selectedCollectionSets) {
            if (selectedCollectionSets[set].isSelected == false) {
                $scope.repetitionService.addSet(set);
            }
            selectedCollectionSets[set].isSelected = true;
        }
    };

    $scope.deselectAllSets = function () {
        var selectedCollectionSets = $scope.selectedCollection.sets;

        for (var set in selectedCollectionSets) {
            if (selectedCollectionSets[set].isSelected) {
                $scope.repetitionService.removeSet(set);
            }
            selectedCollectionSets[set].isSelected = false;
        }
    };

    $scope.startRepetition = function () {
        $location.path("/repetition");
    };
}

function RepetitionController($scope, $location, repetitionService) {
    $scope.sets = repetitionService.getSets();
    $scope.questionsLeft = $scope.sets.length;
    $scope.questionsAnswered = 0;
    $scope.percentageLeft = ($scope.questionsLeft == 0 ? 100 : 0);

    $scope.endRepetition = function () {
        $location.path("/setsAndCollections");
    };
}

now I am in process of converting global function controllers to ones defined by angular API as you can see by example of DashboardController.

Now in my test:

describe("DashboardController", function () {
    var ctrl, scope;

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

    it("has repeats attribute set to 5", function () {
        expect(scope.repeats).toBe(5);
    });
});

I am getting

    Error: Argument 'DashboardController' is not a function, got undefined

I am wondering then, where is my mistake? If I understand this right, ctrl = $controller('DashboardController', {$scope: scope}); should inject my newly created scope to my DashboardController to populate it with attributes - in this case, repeats.

share|improve this question
Are you making sure to include your controllers.js in your test runner? For example in your karma-runner config file? – Xesued Apr 3 at 23:22
I checked, the file is read OK, what is interesting is, that if I uncomment the global function style of DashboardController, this simple test passes so this file should be included. – Andna Apr 3 at 23:30
1  
For what it's worth you shouldn't be hanging elements off your $scope variable. $scope should contain a REFERENCE to your model, but it's NOT your model. e.g. $scope.id should be something like $scope.SetsAndCollectionsModel.id. The reason for this comes from issues trying to set primative types from a child scope to a parent scope. You can watch a video that describes this here. Misko, who created Angular, talks about it in a best practices video as well (sorry, don't have a link directly to the time) – Scott Silvi May 18 at 14:08
Thanks for the video with best practices, will watch it for sure. – Andna May 18 at 17:04

1 Answer

up vote 4 down vote accepted

You need to set up your Prototype module first.

beforeEach(module('Prototype'));

Add that to your test, above the current beforeEach would work.

describe("DashboardController", function () {
  var ctrl, scope;

  beforeEach(module('Prototype'));

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

  it("has repeats attribute set to 5", function () {
    expect(scope.repeats).toBe(5);
  });
});
share|improve this answer
Thanks, that was the missing fragment. – Andna Apr 4 at 7:47

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.