Les tests unitaires avec Angular Js
Tester son code Angular
Angular est un framework javascript très répandu et de plus en plus utilisé dans le monde du web.
En tant que framework Javascript, Angular dispose de toute la panoplie des fonctionnalités nécessaires pour créer une application Web. Cependant étant donné qu’il n’y a pas de compilation, seul votre navigateur est capable de vous donner des informations sur les erreurs et ces informations ne sont malheureusement pas suffisantes pour déboguer quand on travail avec une technologie tel que Angular.
Plusieurs outils se sont bâtis autour d’Angular afin de mettre en place des tests unitaires d’une manière assez simple.Nous allons dans cet article voir comment mettre en place une application Angular couverte par des tests Unitaires.
Les avantages des tests unitaires
Les tests unitaires assurent l’orthogonalité de votre code
Nous dirons d’un code qu’il est orthogonal si il est facilement modifiable, en introduisant la notion de Tests Unitaires sur des fonctionnalités dédiées de votre application vous respecterez automatiquement la notion de séparation des préoccupations (SepartionOfConcerns).Il sera donc plus facile de modifier votre code ou de détecter d’éventuelles régressions.
Passer davantage de temps sur le coeur de votre Application
Une fois que toutes vos fonctionnalités de bases ont été implémentées et testées automatiquement par le biais de tests Unitaires, vous aurez plus de temps pour vous concentrez sur la partie algorithmique (métier) de votre application.
Les Tests Unitaires une base pour la documentation
La convention étant de donner des noms explicites ainsi que d’isoler les comportements de vos tests Unitaires, si votre code est repris par un autre développeur il pourra facilement savoir ce qu’il se passe dans vos méthodes juste en lisant les noms de vos tests unitaires et les résultats de ceux ci.
Angular est conçu pour les tests unitaires
Dans son architecture, AngularJS utilise plusieurs patrons de conception comme par exemple l’injection de dépendance ce qui rend la mise en place des Tests Unitaires facile. On peut ainsi aisément injecter les dépendances des objets que l’on veut tester et mocker leur comportement ou donnée.
Les outils disponibles :
Karma, Jasmine, Angular Mocks, Protractor, Mocha, Chai / Chai-as-promised, Sinon, Browserify, Partialify.
Comme vous pouvez le voir la liste d’outils disponibles pour faire des tests Unitaires est longue, nous allons nous concentrer sur les deux outils les plus fréquemment utilisés (Karma et Jasmine).
Ces deux outils, Karma (le testeur) et Jasmine (le Framework), nous offrent toutes les fonctionnalités dont nous avons besoin pour faire des tests Unitaires sur nos applications.
Comment ça marche ?

Comme nous pouvons le voir dans ce schéma nous allons utiliser Karma/Jasmine et notre code source Angular pour mettre en place nos tests unitaires.
Karma : est un outil qui à été sélectionné par l’équipe AngularJS, afin de tester les fonctionnalités de leur Framework. Ils ont testés Karma et dès lors l’ont recommandé. En plus de son adaptabilité facile avec Angular, Karma peut s’intégrer à un workflow, émuler et établir vos tests sur les différents navigateurs ainsi que sur smartphones et tablettes.
Jasmine : est un framework qui fournit des fonctionnalités permettant de tester Unitairement ses applications Javascript. Comme Karma, Jasmine est un Framework recommandé par l’équipe AngularJS.
Installation de NPM
- Télécharger et installer NPM – Node Package Manager
- Une fois cette étape terminée, tapez la ligne de commande suivante afin de voir la version NPM installée
> npm ‐‐version
3.9.3
Installation de Jasmine
> npm install ‐g yo
> npm install ‐g bower
> npm install generator‐jasmine
Installation de Karma
> npm install ‐g karma
> npm install generator‐karma
> yo karma ‐‐test‐framework=jasmine
Exemple
L’utilisation du framework Jasmine est assez simple nous allons dans la suite de cet article tester une application angular dans un premier temps avec Jasmine, puis nous intégrerons Karma à notre projet.
Tout d’abord vous devez installer le Jasmine generator dans votre projet de test (Le dossier contenant vos sources)
- Placer le fichier .yo-rc.json à la racine du projet que vous voulez tester. Vous devez avoir un object {} vide à l’intérieur de ce fichier..
- Lancer la commande :
> yo jasmine
Deux dossiers sont alors créés dans votre répertoire.
Dans cet exemple nous allons voir les bases. Pour plus d’informations et de détails rendez-vous sur le site de Jasmine => #jasmine
Ouvrez le répertoire “test”, vous verrez qu’il se compose d’un fichier index.html représentant l’interface qui permettra de voir les résultats de vos tests et le dossier “spec” qui contient les fichiers décrivant vos tests Unitaires.

Sources
test/index.html
<!doctype html>
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="stylesheet" href="../bower_components/jasmine‐core/lib/jasmine‐core/jasmine.css" />
</head>
<body>
<script src="../bower_components/jasmine‐core/lib/jasmine‐core/jasmine.js"></script>
<script src="../bower_components/jasmine‐core/lib/jasmine‐core/jasmine‐html.js"></script>
<script src="../bower_components/jasmine‐core/lib/jasmine‐core/boot.js"></script>
<script src="../lib/angular/angular.js"></script>
<script src="../lib/angular/angular‐animate.js"></script>
<script src="../lib/angular/angular‐route.js"></script>
<script src="../lib/angular/angular‐touch.js"></script>
<script src="../lib/angular/angular‐sanitize.js"></script>
<script src="../lib/angular/angular‐mocks.js"></script>
<!‐‐ include source files here... ‐‐>
<script src="../testableModule.js"></script>
<!‐‐ include spec files here... ‐‐>
<script src="spec/test.js"></script>
</body>
</html>
Voici en détail la page index.html, vous devrez ajouter toutes vos dépendances Angular ainsi que vos sources et les sources de vos tests Unitaires.
Ici mes fichiers sont : testableModule.js et tests.js
testableModule.js
// Create module
var myApp = angular.module('myApp', []);
// Controller which counts changes to its "name" member
myApp.controller('MyCtrl', ['$scope', function ($scope) {
$scope.name = 'Freeza';
$scope.counter = 0;
$scope.ChangeName = function (newName) {
$scope.name = newName + " modified by function";
}
$scope.$watch('name', function (newValue, oldValue) {
$scope.counter = $scope.counter + 1;
});
}]);
// Controller with dependencies on Angular's $http service
myApp.service('MyService', function ($http) {
// Returns the result with the response status
//If you you to make an efficient test you should know the return value in advance
this.get= function (id) {
return $http.get('https://jsonplaceholder.typicode.com/posts/' + id).then(
function(result) {
if (result.status == 200) {
return result;
}
}
);
};
});
tests.js
describe('myApp', function () {
var scope,controller;
beforeEach(function () {
module('myApp');
});
describe('Unit : Testing Controller MyCtrl', function () {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope
});
}));
it('sets the name', function () {
expect(scope.name).toBe('Freeza');
});
it('watches the name and updates the counter', function () {
expect(scope.counter).toBe(0);
scope.name = 'Batman';
scope.$digest();
expect(scope.counter).toBe(1);
});
it('modifies the name with function, watches the name', function () {
scope.ChangeName('Batman');
expect(scope.name).toBe('Batman modified by function');
});
});
describe('Unit : Testing Service MyService', function () {
var successCallback, myService, $httpBackend, userResponse, errorCallback;
userResponse = { "userId": 1,"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
};
beforeEach(inject(function(_MyService_,_$httpBackend_) {
angular.module('myApp');
myService = _MyService_;
$httpBackend = _$httpBackend_;
successCallback = jasmine.createSpy();
errorCallback = jasmine.createSpy();
}));
it('should contain a MyService service', (function() {
expect(myService).toBeDefined();
}));
it('should returns http requests response with status and data of the User with UserId = 1', function () {
var UserId = 1;
$httpBackend.expectGET('https://jsonplaceholder.typicode.com/posts/' + UserId).respond(200, userResponse);
myService.get(UserId).then(function(data) {
expect(data.status).toEqual(200);
expect(data.data).toEqual(userResponse);
});
$httpBackend.flush();
});
});
});
Page résultat

Karma
Installation des navigateurs
> npm install karma‐firefox‐launcher
> npm install karma‐chrome‐launcher
> npm install karma‐opera‐launcher
> npm install karma‐ie‐launcher
Installation de Karma dans votre projet
> karma init
Définir la configuration de Karma (vous pouvez saisir les mêmes valeurs) :

Modification du fichier de configuration de Karma

Nous allons modifier ce fichier comme suit :
files: [
'./lib/angular/angular.js',
'./lib/angular/angular‐mocks.js',
'./lib/angular/angular‐route.js',
'./test/spec/*.js',
'*.js'
],
// Add this part if doesn't exist !Which plugins to enable
plugins: [
'karma‐phantomjs‐launcher',
'karma‐jasmine',
'karma‐chrome‐launcher',
'karma‐firefox‐launcher',
'karma‐opera‐launcher',
'karma‐ie‐launcher',
],
browsers: ['Chrome','Firefox']
Lancez vos tests :
> karma start ‐‐single‐run

Visual Studio et Karma
Après avoir créé votre projet sous Visual Studio, placez vous à la racine de votre projet puis installer Node Js avec la ligne de commande suivante :
> npm init
Vous verrez qu’un fichier package.json sera ajouté dans votre solution, vous devrez peut-être avoir à l’inclure manuellement via l’explorateur sous Visual Studio.
Vous pouvez procéder ainsi :
- Copier karma_conf.js à la racine de votre projet. (N’oubliez pas de modifier vos chemins d’accès)
- Copier le repertoire test à la racine de votre projet.
- Placer les fichiers contenant vos sources à tester , ici testableModule.js dans le répertoire www/scripts.
- Modifier le fichier package.json comme suit :
{
"name": "testangular",
"version": "1.0.0",
"dependencies": {
},
"description": "",
"repository": {
"type": "git",
"url": "https://github.com/karma‐runner/gulp‐karma"
},
"bugs": {
"url": "https://github.com/karma‐runner/gulp‐karma/issues"
},
"homepage": "https://github.com/karma‐runner/gulp‐karma",
"devDependencies": {
"karma": "^0.13.0",
"karma‐jasmine": "^0.3.6",
"karma‐firefox‐launcher": "^1.0.0",
"karma‐chrome‐launcher": "^2.0.0",
"karma‐opera‐launcher": "^1.0.0",
"gulp": "^3.9.0"
}
}
5. Création d’un fichier gulpfile.js
Ici nous allons définir une tâche pour l’exécution de nos tests unitaires, il faut noter que gulp offre d’autres types de tâches telles que la minification/concaténation …
// <binding BeforeBuild='testangular' />
var gulp = require('gulp');
var Server = require('karma').Server;
// Run test once and exit
gulp.task('testangular', function (done) {
new Server({
configFile: __dirname + '/karma.conf.js',
singleRun: true
}, done).start();
});
Lancez vos tests sous Visual Studio
Une fois le fichier gulpfile.js créé et initialisé comme vu précédemment. Dans Visual Studio allez dans the Affichage > Autres fenêtres > Explorateur d’exécution de tâche.
Voici ce que vous devriez voir apparaître (la liste des tâches configurées dans votre fichier gulpfile.js)

Conclusion
Que pensez vous des tests Unitaires ?
Pensez-vous que vos applications angular seraient plus robustes si vous les testiez Unitairement ?
Auriez vous d’avantages d’informations à ajouter à cet article ?
Envoyez nous un email ou laissez un commentaire pour nous faire part de vos impressions..