Tester notre application est une partie fondamentale du processus de développement. Souvent l’accent est mis sur d’autres phases de développement d’application, mais le test est sans aucun doute la phase qui garantit sa qualité et sa fiabilité…

Différence entre tests unitaires et tests fonctionnels

  • Tests unitaires
    L’objectif d’un test unitaire est d’isoler une partie du code pour le tester. Cela permet de s’assurer qu’une fonctionnalité précise est opérationnelle. Dans les applications non critiques, l’écriture des tests unitaires a longtemps été considérée comme une tâche secondaire. Cependant, les méthodes Extreme programming (XP) ou Test Driven Development (TDD) ont remis les tests unitaires au centre de l’activité de programmation.Une application étant la combinaison de multiples fonctionnalités, il arrive souvent que des modifications sur une fonctionnalité en altèrent une autre. C’est à ce moment-là qu’il est intéressant d’avoir des tests unitaires qui permettent de s’assurer que chaque brique continue de fonctionner correctement tout au long du processus de développement, indépendamment de ses dépendances.

 

    1. Objectifs
      – Tester les unités de code de manière isolée.
      – Vérifier si le code est correct.
      – Tester chaque fonction ainsi que la procédure.
      – Détecter les erreurs plus tôt et les corriger dans le cycle de développement tout en réduisant les coûts.
      – Aider les développeurs à comprendre le code de base et leur permettre d’apporter rapidement des modifications.
      – Aider également à la réutilisation du code.
    2. Avantages
      – Connaître la fonctionnalité fournie par une unité et expliquer comment l’utiliser pour acquérir une compréhension de base de l’API de l’unité.
      – Affiner le code et s’assurer que le fonctionnement du module est approprié.
      – Tester les parties du projet sans se reposer sur d’autres pour l’achèvement.
    3. Quand utiliser les tests unitaires ?
      – Ils doivent être effectués avant la mise en production du logiciel, sinon ils font perdre du temps qui pourrait être consacré à l’écriture de nouvelles fonctionnalités, à la correction des anomalies ou à l’amélioration d’autres domaines.
      – Ils sont également particulièrement utiles lors du développement de bibliothèques de code réutilisable, car celles-ci peuvent être utilisées à l’infini dans différents projets sans provoquer d’interactions indésirables avec d’autres parties du système.
  • Tests fonctionnels
    Les tests fonctionnels sont généralement écrits par des analystes métier, des QA et des testeurs pour vérifier que l’application logicielle fonctionne correctement du point de vue de l’utilisation.Ils sont définis comme une méthode permettant de tester la fonctionnalité d’une application logicielle. Le plus souvent, ils sont utilisés pour vérifier des scénarios ou des modèles d’utilisation de bout en bout. Les tests fonctionnels peuvent aller du simple chargement de page jusqu’aux critères d’acceptation complets. Des documents d’exigences détaillés sont souvent nécessaires pour comprendre pleinement ce qui doit être testé.
    L’exécution des tests fonctionnels peut prendre plus de temps que celle des tests unitaires et peut impliquer l’utilisation de dépendances externes.
    1. Objectifs
      Permettre de détecter les défauts qui pourraient être oubliés par les programmeurs lors du développement du logiciel.
      – Permettre de gagner en confiance et fournir des informations sur le niveau de qualité.
      – Garantir un résultat qui répond aux attentes de l’entreprise et de l’utilisateur.
    2. Avantages
      Évaluer les performances et les fonctionnalités d’une application logicielle avant de la livrer au client.
      – Construire des scénarios de test représentant les scénarios d’utilisation du monde réel.
      – Répondre à toutes les exigences/demandes de l’utilisateur et du client.
      – Améliorer l’utilisation réelle du système.
      – Améliorer la qualité du produit logiciel.
    3. Quand utiliser les tests fonctionnels ?
      Ils sont lancés une fois que tout le code a été écrit et testé de manière unitaire, car les exigences fonctionnelles sont souvent complexes et plus difficiles à évaluer.
      – Ils sont utilisés après les tests unitaires lorsqu’un développeur a des difficultés à faire fonctionner l’implémentation d’une méthode comme prévu.
      – Les tests fonctionnels sont lents et complexes, mais ils garantissent que le système fonctionnera conformément aux exigences. Il nous aide à identifier les problèmes/défauts dans la fonctionnalité.

Tests unitaires Angular

  • Mettre en place karma pour vos test unitaires Angular
    1.  Karma
      Karma est un outil de terminal JavaScript qui permet le lancement de navigateurs web. Une fois le navigateur lancé, Karma y charge le code de l’application et exécute vos tests.Il est possible d’utiliser Karma afin de lancer votre application sur plusieurs navigateurs (Chrome, Safari, Firefox, PhantomJS, …). Cela vous permet de vérifier que votre application fonctionne bien partout.Lors de l’exécution de vos tests, les résultats sont affichés directement dans votre terminal.
    2. Quand utiliser Karma
      • Exécuter notre application dans de vrais navigateurs.
      • Tester notre application dans plusieurs navigateurs (bureau, mobile et tablette).
      • Exécuter nos tests localement, lors du développement.
      • Exécuter nos tests sur un serveur d’intégration continue.
      • Exécuter nos tests automatiquement quand nous sauvegardons un fichier.
    3. Configuration de Karma
      La configuration de Karma se trouve dans le fichier karma.conf.js. Voici un exemple de configuration documenté :
<script type = "text/javascript" id ="lp-theme-script-js-extra">

// http://karma-runner.github.io/0.10/config/configuration-file.html
module.exports = function(config) {
config.set({
  // Chemin de base pour la résolution des fichiers à inclure
  basePath: '',’

  // Nom du cadriciel de tests à utiliser (jasmine/mocha/qunit/...)
  frameworks: ['jasmine'],
  // Liste des fichiers à charger dans le navigateur
  files: [
    // bower:js
    'client/bower_components/angular/angular.js',
    // endbower
    'client/app/app.js',
    'client/{app,components}/**/*.js',
  ],
  // Port du serveur web
  port: 8080,
  // Création de rapports
  // - dots
  // - progress (default)
  // - spec (karma-spec-reporter)
  // - junit
  // - growl
  // - coverage
  reporters: ['spec'],
  // Lance les tests automatiquement lors d'une modification 
  // à un fichier (si à true)
  autoWatch: false,
  // Lance les navigateurs suivants:
  // - Chrome
  // - ChromeCanary
  // - Firefox
  // - Opera
  // - Safari (only Mac)
  // - PhantomJS
  // - IE (only Windows)
  browsers: ['PhantomJS'],
  // Active le mode intégration continue
  singleRun: false
});};
</script>

Si le projet n’est pas configuré avec Karma, nous pouvons utiliser la commande suivante. On nous guidera afin de créer notre fichier de configuration.
karma init my.conf.js

Il est simple d’exécuter nos tests dans plusieurs navigateurs.
Nous n’avons qu’à ajouter le nom du navigateur supporté à notre tableau browsers :browsers: ['PhantomJS', 'Chrome'], et lancer nos tests 

$ grunt karma
 
Running "karma:loop" (karma) task
12 02 2016 11:47:39.903:INFO [karma]: Karma v0.13.19 server started
12 02 2016 11:47:39.922:INFO [launcher]: Starting browser PhantomJS
12 02 2016 11:47:39.969:INFO [launcher]: Starting browser Chrome
  • Ecrire votre premier test unitaire avec Jasmine

a. Jasmine
Jasmine est un framework de tests open source pour JavaScript. Il possède une syntaxe assez facile à prendre en main, finalement assez proche de RSpec pour Ruby, un framework dit Behaviour-Driven Development (BDD). Il est devenu le choix le plus populaire pour tester des applications Angular.
Jasmine offre des fonctionnalités qui nous aident à structurer nos tests et à exécuter des vérifications (assert).

b. Mon premier test
Une suite de tests Jasmine débute avec un appel à describe, une fonction globale de Jasmine qui prend en considération deux paramètres : une chaîne de caractères et une fonction. La chaîne de caractères représente un titre pour la suite, rappelant généralement ce qui va être testé. La fonction, quant à elle, est un bloc de code qui implémente la suite.

Les « spécifications », appelées simplement specs, sont définies par l’appel de la fonction globale it de Jasmine, qui elle aussi prend en paramètre une chaîne de caractères et une fonction. La chaîne de caractères définit le test et la fonction est le test.

Une spec peut contenir une à plusieurs expectations
En Jasmine, une expectation est une assertion pouvant être vraie ou fausse. Un test qui passe est un test possédant toutes ses expectations à vrai. Un test qui échoue possède au moins une expectation à faux.

<script type="text/javascript"> 
describe("My first suite", function() {
  it("contains spec with an expectation", function() {
    expect(true).toBe(true);
  });
});
</script>

Il est possible d’exécuter du code « avant » ou « après » chacune des specs écrites, respectivement grâce aux fonctionnalités beforeEach et afterEach.

<script type="text/javascript"> 
describe("My first suite with 'beforeEach' and 'afterEach'", function() { 
  var a = 0;

  beforeEach(function() {
    a += 1;
  });
  afterEach(function() {
    a = 0;
  });

  it("checks the value of a", function() {
    expect(a).toEqual(1);
    a += 1;
  });
  it("expects a to still be equal to 1", function() {
    expect(a).toEqual(1);
  });
});
</script>

Il est aussi possible d’exécuter du code « avant » ou « après » toutes les specs écrites, respectivement grâce aux fonctionnalités beforeAll et afterAll.

<script type="text/javascript"> 
describe("My first suite with 'beforeAll' and 'afterAll'", function() {
  var a;

  beforeAll(function() {
    a += 1;
  });
  afterAll(function() {
    a = 0;
  });

  it("checks the value of a", function() {
    expect(a).toEqual(1);
    a += 1;
  });
  it("does not reset a between specs", function() {
    expect(a).toEqual(2);
  });
});
</script>

Les matchers:
Un matcher produit une comparaison booléenne entre la valeur réelle d’un élément et sa valeur attendue.
Exemples: not, toBe, toEqual, toMatch, toBeNull, toContain, toBeTruthy, toBeFalsy, toBeDefined, toBeUnDefined, toBeLessThan, toBeGreaterThan, toBeCloseTo….

  • Mettre en place Jest pour vos tests unitaires Angular

a. Jest
Jest est un framework de test complet créé par Facebook. Il vient avec tout le bagage nécessaire pour réaliser des tests unitaires pour nos applications JavaScript.

Il comprend:
– D’importantes capacités de mock
– Une bibliothèque d’assertion intégrée
– Un outil de code coverage intégré
– JSDOM, une librairie émulant le navigateur

b. Installation et configuration
– Pour installer les dépendances requises, lancer la commande suivante dans un terminal :
npm install jest jest-preset-angular @types/jest --save-dev

– Si vous utilisez Yarn, la commande à lancer est :
yarn add --dev jest jest-preset-angular @types/jest

– Créez le fichier jest.config.js à la racine de votre projet avec le contenu suivant :

<script type="text/javascript"> 
const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig');

module.exports = {
   preset: 'jest-preset-angular',
   roots: ['<rootDir>/src/'],
   testMatch: ['**/+(*.)+(spec).+(ts|js)'],
   setupFilesAfterEnv: ['<rootDir>/src/test.ts'],
   collectCoverage: true,
   coverageReporters: ['html'],
   coverageDirectory: 'coverage/my-app',
   moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths || {}, {
  prefix: '<rootDir>/',
}),
};
</script>     

Le fichier jest.config.js contient la configuration de Jest. Avec cette configuration, le framework va :

  • Prendre tous les fichiers .spec.ts se trouvant dans le dossier src de notre projet
  • Lire le fichier de configuration de TypeScript tsconfig.json à la recherche d’alias pour qu’on puisse utiliser les alias dans nos tests
  • Transpiler à la volée notre code TypeScript avant de lancer les tests
  • Collecter les informations sur la couverture de code et les écrire dans le dossier coverage/my-app
  • Ouvrez le fichier src/test.ts et remplacer son contenu par :
<script type="text/javascript"> 
import 'jest-preset-angular';
Object.defineProperty(window, 'CSS', { value: null });
Object.defineProperty(window, 'getComputedStyle', {
  value: () => {
    return {
      display: 'none',
      appearance: ['-webkit-appearance'],
    };
  },
});
Object.defineProperty(document, 'doctype', {
  value: '<!DOCTYPE html>',
});
Object.defineProperty(document.body.style, 'transform', {
  value: () => {
    return {
      enumerable: true,
      configurable: true,
    };
  },
});
</script>  

Ce bout de code fait deux choses:

– Importer le module JavaScript jest-prest-angular pour mettre en place notre environnement de test Angular
– ‘Mock’ certaines propriétés et fonctions de l’objet global window pour s’assurer que nos tests peuvent bien tourner dans un environnement JSDOM

Ouvrez le fichier tsconfig.spec.json et mettez à jour son contenu :

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/spec",
    "types": [
      "jest", // 1
      "node"
    ],
    "esModuleInterop": true, // 2
    "emitDecoratorMetadata": true // 3
  },
  "files": ["src/test.ts", "src/polyfills.ts"],
  "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}

Ces changements permettent de :

– Prendre en compte le fichier de déclarations de types de Jest
– Activer l’option esModuleInterop du compilateur TypeScript pour empêcher  beaucoup d’avertissements dans  la console
– Activer l’option emitDecoratorMetadata qui autorise l’injection de dépendances Angular

 

Jest vs Karma

Jest
offre une bien meilleure expérience développeur.

  • Plus rapide que Karma avec une parallélisation de l’exécution des tests
  • Très bien documenté avec une large communauté derrière
  • Plus intelligent que Karma (On peut relancer les tests affectés )
  • Un toolkit complet pour les tests unitaires
  • Headless par défaut grâce à la librairie JSDOM
  • Karma n’est qu’un élément d’une longue liste de dépendances nécessaires pour mettre en place des tests JS alors que Jest inclut tous les outils pour créer une stack basique et efficace (comprenant le moteur, le framework, le système d’assertions et de mocks avec également le code coverage) ce qui rend notre système de test bien plus simple à maintenir
  • Une installation unifiée pour une configuration simplifiée
  • Comment lancer les tests unitaires ?

Pour exécuter les tests unitaires d’une application angular, il suffit d’exécuter la commande suivante dans le terminal ng test

Après avoir lancé les tests, un message de succès ou d’erreur s’affiche sur la console.

Conclusion

Nous venons de voir la nécessité des tests unitaires dans nos applications en se focalisant sur la configuration de certains outils comme Karma, Jasmine et Jest afin de tester unitairement la logique métier de notre application Angular qui dispose de tous les outils nécessaires pour maîtriser le test de nos fonctions, méthodes et appels asynchrones.

Partager
Faire suivre