unit-testing angularJS controllers in a non-trivial environment

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();
		});
	});
});

Leave a Reply

Your email address will not be published. Required fields are marked *

*