Single Page Applications mit AngularJS, Teil 3: Unit-Testing

Seite 3: Controller

Inhaltsverzeichnis

Der Entwickler testet mit Jasmine Controller ähnlich wie ViewModels. Allerdings muss er hierzu wissen, wie er an eine Instanz des definierten Controllers gelangt. Dazu besorgt er sich vom Injector den Service $controller – eine Funktion, die den Controller zur Ausführung bringt. Sie nimmt den Namen des Controllers sowie ein Objekt mit den Parametern entgegen, die an ihn zu übergeben sind. Die Eigenschaften des Objekts haben den Namen der einzelnen Parameter zu entsprechen. Auf diese Weise kann der Entwickler den Controller unter Verwendung eines eigenen Scopes ausführen lassen. Danach kann er prüfen, ob der Scope die gewünschten Werte enthält und eventuelle Funktionen beziehungsweise ViewModels des Scope-Objekts testen.

Ein Beispiel für den Test eines Controllers mit AngularJS sähe beispielsweise wie folgt aus:

describe("FlugBuchenVM", function () {

var scope;

beforeEach(function () {

var injector = angular.injector(['ng', 'Flug', 'ui.router']);

var http = injector.get("$http");
var q = injector.get("$q");
var rootScope = injector.get("$rootScope");
scope = rootScope.$new();
var controller = injector.get("$controller");

controller("FlugBuchenCtrl", { $scope: scope });

scope.vm.baseUrl = "http://localhost:59978";


});

afterEach(function () {
});

it("should load Passagiere", function () {

var finished = false;
var error = false;

var vm = scope.vm;

runs(function () {
vm.passagierNrFilter = "1";
vm.load().then(function () {
finished = true;
}).catch(function (ex) {
finished = true;
error = true;
});
});

waitsFor(function () {
return finished;
}, "Zu lange auf Load gewartet!", 6000);

runs(function () {
expect(error).toBe(false);
expect(vm.passagiere.length).toBe(1);
});
});
});

Mit dem im Lieferumfang von AngularJS enthaltenen Skript angular-mocks.js unterstützt AngularJS den Entwickler beim Erstellen von Tests mit Jasmine. Es enthält ein paar Hilfsfunktionen, die den Einsatz des Injectors vereinfachen. Daneben bewirkt das Einbinden des Skripts, dass zentrale AngularJS-Services durch Mocks (Attrappen) ausgetauscht werden. Zu diesen zählt ein Mock für den Service $httpBackend, der den Service $http zum Zugriff auf HTTP-Services heranzieht. Um ein isoliertes Testen zu ermöglichen, liefert er stattdessen Objekte zurück, die der Testfall im Vorfeld definiert hat, wie Listing 2 demonstriert. Dabei simuliert der Mock den asynchronen Service-Zugriff durch synchrone Funktionsaufrufe. Das vereinfacht das Schreiben der Tests, zumal sich der Entwickler nun nicht mehr mit Aufrufen von runs und wait belasten muss.

Die von angular-mock.js bereitgestellte Funktion module definiert, dass das Modul Flug zu testen ist. Da module innerhalb von beforeEach zum Einsatz kommt, ist das Modul vor jedem Testfall zu initiieren. Beim Einsatz von angular-mock.js muss der Entwickler das Modul ng nicht explizit referenzieren. An einen zweiten Aufruf von beforeEach ĂĽbergibt das betrachtete Beispiel einen Aufruf von inject, einer weiteren angular-mock.js-Funktion. Sie erwartet eine Funktion, an die sie delegiert.