I just released a new patch version of my angularJS sekeleton project ngStart. It now contains a sample unit test for a controller.
There are many examples out there how to unit test an angularJS controller. Basically you have to create a new controller scope yourself and tell angularJS to create a new scope instance for a given controller. This looks usually like this (as a jasmine test):
describe("the controller", function () { var contactController, scope; beforeEach(function () { inject(function ($rootScope, $controller) { scope = $rootScope.$new(); contactController = $controller("contactController", {$scope: scope}); }); }); it("should be something", function () { expect(scope.someProperty).toBeDefined(); }); });
Note: this code assumes that the controller was registered in the production code via a call to angular.controller like this: angular.controller(“contactController”, function($scope){…}
The catches in my ngStart project are, that
- its a requireJS environment and
- that the controller is not registered by name as its usually done in simple projects, but the controller is defined inside a route definition without an explicit name. The controller is only valid for the given route. As far as I know, you can’t access it via a name anywhere outside.
The route definition looks like this:
define(['angular', 'ContactController'], function (angular, ContactController) { var contact = angular.module("contact", []); contact.config(["$routeProvider", function($routeProvider) { $routeProvider.when('/contact/', { templateUrl: 'contact.html', controller: ContactController }); }]); return contact; });
How do you test the controller now in a unit test if you can’t access it via name?
Easy.
As we can load the controller definition with requireJS (aka its function representation) you can give the $controller function this controller function directly. You don’t have to provide a string which angularJS uses to look for a registered function like its done with a call to angular.controller(“name”, controllerFunction).
The rest is the standard behaviour like above:
define(["contact/ContactController"], function(ContactController) { describe("the controller", function () { var contactController, scope; beforeEach(function () { inject(function ($rootScope, $controller) { scope = $rootScope.$new(); contactController = $controller(ContactController, {$scope: scope}); }); }); it("should be something", function () { expect(scope.someProperty).toBeDefined(); }); }); });