5

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.

4
  • Are you making sure to include your controllers.js in your test runner? For example in your karma-runner config file? Commented Apr 3, 2013 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. Commented Apr 3, 2013 at 23:30
  • 3
    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) Commented May 18, 2013 at 14:08
  • Thanks for the video with best practices, will watch it for sure. Commented May 18, 2013 at 17:04

1 Answer 1

19

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);
  });
});
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that was the missing fragment.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.