Tous les articles par Julien

.NET : Améliorez vos tests unitaires avec Moq et NFluent

Une des bonnes pratiques d’un projet informatique est la mise en place de tests unitaires. Je vais vous présentez 2 librairies, disponible sous forme de package NuGet, pour vous aidez dans l’écriture de tests.

Moq

Le but d’un test unitaire est de tester un composant isolé, d’où la nécessité d’utiliser une librairie permettant de faire du Mock. J’utilise pour cela Moq.

Les exemples de tests suivant utilisent NUnit comme framework de tests unitaires. Afin de pouvoir facilement remplacer les dépendances des classes à tester, j’utilise Unity comme container d’injection de dépendance (IoC). Vous allez ainsi pouvoir facilement passer à votre classe dans le constructeur votre Mock.

Voyons comment configurer et effectuez des assertions sur vos mock avec Moq :

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using NFluent;
using NUnit.Framework;

namespace Demo.Tests
{
    [TestFixture]
    public class MyServiceTests : BaseServicesTests
    {
        private Mock<IMyService> myServiceMock;
        private Mock<ILogger> loggerMock;
        
        private IMyClass myClass;

        [SetUp]
        public override void SetUp()
        {
            base.SetUp();

            //Create Mocks
            myServiceMock = new Mock<IMyService>();
            loggerMock = new Mock<ILogger>();
                                  
           //We pass our mock to the instance to test
           myClass = new MyClass(myServiceMock, loggerMock);
        }

        [Test]
        public void MyMethodShouldReturnTrue()
        {
            //Setup method on service to return expected value to test our case
            //Here we setup to return an empty list of string
            myServiceMock.Setup(m => m.GetData()).Returns(new List<string>());
            
            var result = myClass.MyMethod();
            //Verify the result is what is expected
            Check.That(result).IsTrue();
            //Verify that our mock has been called
            myServiceMockSetup.Verify(m => m.GetData(), Times.Once);
        }
    }
}

Moq fournit un ensemble de méthodes permettant de configurer (Setup + Returns/Callbacks) et d’effectuer des tests sur les mocks (Verify).
On peut par exemple vérifier le nombre d’appel d’une méthode, les paramètres, …

Voici comment vérifier qu’un méthode prenant un paramètre de type string a bien été appelé 2 fois avec comme valeur de paramètre « MyTestString »

myServiceMock.Verify(r => r.MyMethod(It.Is<string>(x => x == "MyTestString")), Times.Exactly(2));

Le guide de démarrage de Moq

NFluent

Comme vous avez pu peut être le remarquer, je fais pas mal de JavaScript en ce moment et j’aime beaucoup les librairies de tests style jasmine, should, … qui permettent de décrire les assertions de manière plus lisibles. En .Net, j’utilise NFluent qui permet d’avoir une API d’assertion fluent où l’on peut chaîner les assertions à la suite les une des autres.

Check.That(julien)
    .IsNotNull()
    .And.IsNotEqualTo(david)
    .And.IsInstanceOf<Person>();

Dans l’exemple ci-dessus, je vérifie que le l’objet julien n’est pas null, n’est pas égal à david et que cet objet est une instance de la classe Person.

D’autres exemples d’assertions :

//Collections
//Nombre d'élément d'une collection
Check.That(persons).HasSize(3); 
//Il existe une personne ayant la propriété Name égale à Julien
Check.That(persons.Extracting("Name")).Contains("Julien"); 

//Test des exceptions
Check.ThatCode(() => myService.MyMethod())
    .Throws<MyException>()
    .WithMessage("MyMessage")
    .And.WithProperty("MessageKey", "MyMessageKey");

Voici des exemples d’assertion avec NFluent.

Liens

Bon tests 🙂 …

ES6 : quelques nouveautés de la prochaine version de JavaScript – Part II

Cet article est la suite de ES6 : quelques nouveautés de la prochaine version de JavaScript.

Modules

La gestion de modules/dépendances est un vrai manque dans JavaScript. Des solutions comme AMD ou CommonJs ont permis de combler ce manque. La nouvelle version de JavaScript apporte une gestion native des modules.

Un module est défini dans un fichier. Les fonctions de ce fichier ne sont pas visibles des autres fichier à moins de les exporter explicitement. Cela se fait via l’introduction de 2 nouveaux mots clés : import et export. Export permet de définir ce que vous voulez exposer. A l’opposé,  import permet de d’importer tout ou partie d’un module.

Déclarons un fichier utils.js qui contient une fonction permettant de générer un Uuid

function generateUUID () {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r &amp;amp; 0x3 | 0x8);
        return v.toString(16);
    });
};

export { generateUUID }

Importons la méthode generateUUID dans un autre module

import { generateUUID } from 'utils';

var uuid = generateUUID();

On peut même importer le module complet

import 'utils' as utils;
 
console.log(utils.generateUUID());

Quelques liens pour aller plus loin :

Angular 2.0 utilisera les modules ES6.

Classes

ES6 apporte la gestion des classes via le mot clé class (avec d’autres comme constructor et extends). JavaScript est déjà objet via les prototypes mais peu de gens savent les utiliser.
Les nouveaux mots clés sont juste du « sucre syntaxique » au dessus des prototypes afin de faciliter l’écriture sous forme de classes, plus familier des personnes venant de Java, C#, ….

Pour ceux qui s’intéressent au développement objet via les prototypes, je conseille la présentation de Christophe Porteneuve, portant notamment sur les prototypes, à Paris Web : slides et vidéo.

Voici un exemple de la définition d’une classe Employee héritant de Person.

class Person {
    constructor(firstName, lastName, age) { //constructors!
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    fullName(){
        console.log(this.firstName + &amp;quot; &amp;quot; + this.lastName);
    }
}

class Employee extends Person { //inheritance
    constructor(firstName, lastName, age, salary) {
        super(firstName, lastName, age); //call the parent constructor with super
        this.salary = salary;
    }

    printSalary(){
        console.log('Salary : ' + this.salary);
    }
}

let julien = new Employee('Julien', 'Roy', '33', 150000);

julien.fullName();
julien.printSalary();

Quelques liens pour aller plus loin avec les classes ES6.

Améliorations sur les objets

ES6 apporte également des raccourcis concernant la création d’objet concernant :

la création de méthode

//ES5
var obj = {
        myMethod: function () {
            ···
        }
    };
//ES6
var obj = {
        myMethod() {
            ···
        }
    };

les propriétés

Il n’est plus necessaire de spécifier la valeur si on créer une propriété à partir d’une variable.

let x = 4;
let y = 1;
let obj = { x, y }; //ES6
let es5Obj = { x : x; y : y};

Computed property keys

Il est maintenant possible de créer des propriétés ayant une clé composé lors de la création d’un objet.

let obj = {
        [propKey]: true,
        ['b'+'ar']: 123
    };

De nouvelles méthodes sont également disponible :

Object.is()

Object.is permet une comparaison supprimant des cas bizarres en js lors des comparaison avec ===

(NaN !== NaN) //false 
-0 === +0 //false
var isSame = Object.is(value1, value2);

Object.assign()

La méthode assign permet la copie les propriétés d’un objet source dans un autre objet (idéal pour les options par défaut ou le clone d’objet).
Cette méthode existait dans plusieurs librairies comme $.extend en jQuery ou _.defaults pour Underscore/lodash.

Exemple de copie d’un objet avec Object.assign()

var obj = { a: 1 };
var copy = Object.assign({}, obj);

Quelques liens pour aller plus loin :

Symbols

ES6 apporte un nouveau type primitif : les Symbols. Ils sont uniques (2 symbols ne sont jamais égaux) et immutables (non modifiables). Ils peuvent être utilisés pour définir une propriétés d’un objet par exemple (on utilisera la méthode Object.getOwnPropertySymbols() pour obtenir les propriétés de type symbole d’un objet).

//Création d'un symbol
let symbol1 = Symbol();

//Utilisation d'un symbol comme propriété d'un objet
const MY_KEY = Symbol();
let obj = {
    [MY_KEY]: 123
};

Quelques liens sur le sujet :

Références

Quelques liens en complément de ceux de l’article précédent :

la suite au prochain épisode …

Outils CSS3 – HTML5 – JavaScript : Episode 9

Au menu, des liens, des outils, des présentations … sur HTML, CSS et JavaScript.

Talks

Voici quelques présentations que j’ai beaucoup appréciées :

HTML

CSS

  • Pure : Un framework CSS (gère le responsive)
  • Responsive Web Design
  • PlentyofFrameworks : trouver le framework CSS qui vous convient
  • Enjoycss : éditeur de composant CSS
  • CSSShake : Une feuille de style pour secouez vos éléments
  • Style Guide guide : site regroupant des générateurs de style guide (guide d’utilisation de vos style (un exemple))
  • CSS Values : permet de rechercher une propriété CSS pour facilement connaître son utilisation et les valeurs possibles
  • Bounce.js : un générateur d’animation CSS 3
  • CSS Stats : Statistique concernant CSS (nb de sélecteurs, couleurs, …) d’un site
  • Critical Path CSS Generator : Générateur de Critical Path. Critical Path est une technique qui consiste à injecter, directement dans votre code HTML(inline, dans le header) le code CSS minimal afin d’accélérer la vitesse de rendu de votre page. Le reste du code CSS est à charger via des fichiers CSS classique en fin de page. Cet outils permet d’extraire le code CSS à injecter en fonction d’une page.
  • Flexplorer : un générateur et outils de test de flexbox
  • une cheatsheet sur la propriété flexbox en css3

JavaScript

Divers

Underscore/Lo-Dash : Utilisation de _.chain

J’ai présenté dans un article précédent la bibliothèque utilitaire underscore (et son fork lo-Dash). Je vais ici présenter la méthode chain qui permet de facilement chaîner vos appels pour simplifier l’écriture de votre code.

Imaginons que nous avons une liste de documents et que nous souhaitons récupérer le nom des 10 documents les plus récents (en utilisant la propriété lastUpdate).

On pourrait écrire :

//on tri par la propriété lastUpdate
var orderedDocuments = _.sortBy(documents, 'lastupdate');
//on inverse 
var descOrderedDocuments = _.reverse(orderedDocuments);
//on prend les 10 premiers
var last10Documents = _.first(descOrderedDocuments, 10);
//on récupère leur nom 
var last10DocumentNames = _.pluck(last10Documents, 'name');

On peut voir ici que l’on doit créer des variables temporaires afin de passer le résultat d’une méthode à la méthode suivante, ce qui a pour conséquence de polluer le namespace avec des variables qui ne sont utilisées qu’une fois et d’utiliser de la mémoire inutilement. De plus, si vous devez modifier l’ordre d’appels, vous devrez vous assurer de passer la bonne variable temporaire au bon moment.

Voyons comment utiliser _.chain afin de simplifier et de rendre plus lisible ce code. Il faut d’abord appeler chain. Chain va retourner un wrapper (au lieu du résultat) ce qui va nous permettre d’appeler d’autres méthodes qui vont modifier le résultat. Le résultat de la méthode précédente est passé automatiquement à la fonction suivante. Une fois toutes les opérations terminées, il suffit d’appeler value() pour obtenir le résultat.

var lastDocuments = _.chain(documents)
    .sortBy('lastupdate')
    .reverse()
    .first(10)
    .pluck('name')
    .value();

Le code s’en trouve plus lisible et plus maintenable.

Pour ceux qui font du .NET, cela se rapproche de ce qu’on peut faire avec Linq.

ES6 : quelques nouveautés de la prochaine version de JavaScript

JavaScript est basé sur la norme EcmaScript. La version 6, appelée Harmony, va apporter beaucoup de nouveautés qui vont améliorer et combler certains manques du langage.
Voici quelques améliorations que je trouve très intéressantes :

let

Let est le nouveau var. Il permet de déclarer une nouvelle variable mais avec un scope (portée) local contrairement à var.

Ainsi avec var, la portée est soit globale soit liée à la fonction dans laquelle celle-ci est définie. Let permet d’avoir une granularité plus fine (for, if, …) comme il l’est dans d’autres langages.

var t = 1;
if (true) {
  let t = 3;
  for (; t <= 5; t++) {
  	console.log(t);
  }
}
console.log(t);

// 3
// 4
// 5
// 1

Le lien vers la documentation de let sur MDN et un article complet sur le sujet : Variables and scoping in ECMAScript 6

Arrow functions

Les Arrow function est une nouvelle façon de définir des fonctions, un peu comme les expressions lambda en C#. Cela peut s’avèrer pratique dans le cas de définition de callback par exemple.

Une arrow function se définit via =>. La déclaration des paramètres se fait avant la flèche. Si il y a plusieurs paramètres, on entourera les paramètres par des parenthèses. Si l’expression est sur une ligne, celle-ci sera retournée. Si elle est sur plusieurs lignes, celle-ci doit être entourée d’accolades.

Voici un exemple d’un map avec une fonction qui met les valeurs au carré.

var myValues = [1,2,3,4,5];

var squared = myValues.map(x => x*x);
console.log(squared);  //-> [1, 4, 9, 16, 25]

//définition d'un function qui permet de déterminer si n nombre est pair
var even = (x) => x % 2 == 0;
var evenValues = myValues.filter(even);
console.log(evenValues);  //-> [2, 4]

var complex = (a, b) => {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

Le lien vers la documentation des arrow functions sur MDN.

Rest Parameters

Les rest parameters permettent de définir une fonction qui prend un  nombre infini de paramètres sous la forme d’un tableau.

On définit notre dernier paramètre comme étant un rest parameter en ajoutant … devant le nom du paramètre. Nous ne sommes plus obligé de passer par arguments. L’avantage étant ici que notre paramèter est nommé et qu’il s’agit d’un véritable array et qu’on peut donc lui appliquer ses méthodes spécifiques (reduce, sort, map, …).

On peut appeler notre méthode de 2 façons :

  • en passant la liste sous forme de paramètres à la suite les uns des autres
  • en passant la liste sous forme d’un tableau. Dans ce cas, il faut le préfixer par …

Voici un exemple (issue de la page MDN concernant les Rest Parameters et compléter avec l’appel sous forme de tableau)

function multiply(multiplier, ...theArgs) {
  return theArgs.map(function (element) {
    return multiplier * element;
  });
}

var arr = multiply(2, 1, 2, 3); 
console.log(arr); // [2, 4, 6]

var arr = multiply(2, ...[1, 2, 3]); 
console.log(arr); // [2, 4, 6]

Cela ressemble aux params en C#.

Default Parameters

ES6 va permettre de donner des valeurs par défaut aux paramètres de vos fonctions. Cela se fait comme dans d’autres langagues de la façon suivante :

function sayMsg(msg='This is a default message.') {
	console.log(msg);
}
sayMsg();
sayMsg('This is a different message!');

Le lien vers la documentation des default parameters sur MDN.

Array comprehension

Array comprehension est une nouvelle syntaxe pour créer des tableaux à parir de tableaux existant.

Pour cela on va utiliser la syntaxe suivante :

var square = [for (i of [ 1, 2, 3 ]) i*i ]; 
// [ 1, 4, 9 ]

var abc = [ 'A', 'B', 'C' ];
var lowChar = [for (letters of abc) letters.toLowerCase()];
// [ 'a', 'b', 'c' ]

//On peut rajouter des conditions
[for (i of [1, 2, 3, 4]) if (i > 3) i];
// [ 1, 2 ] 

//utiliser plusieurs tableaux
var cross = [for (i of [1, 2, 3]) for (j of [a, b, c]) i+j];
// [ '1a', '1b', '1c', '2a', '2b', '2c', '3a', '3b', '3c']

Le lien vers la documentation des Array comprehension sur MDN.

Generators

Les generators sont des itérateurs spéciaux qui permettent de récuperer la valeur suivante que lors de l’appel à next.

Il introduit le mot clé yield qui permet de définir la valeur retournée. Lors du permier appel à la fonction Generator, on récupère l’itérateur. La récupération d’une nouvelle valeur se fait via next(). Une exception StopIteration est lancée si il n’y pas de nouvelle valeur. Lors d’un appel à next, la fonction continuera son exécution à partir du dernier yield atteint.

Un Exemple tiré d’un article sur les Iterators et Generators sur MDN.

function simpleGenerator(){
  yield "first";
  yield "second";
  yield "third";
  for (var i = 0; i > 3; i++)
    yield i;
}

var g = simpleGenerator();
print(g.next()); // prints "first"
print(g.next()); // prints "second"
print(g.next()); // prints "third"
print(g.next()); // prints 0
print(g.next()); // prints 1
print(g.next()); // prints 2
print(g.next()); // StopIteration is thrown

Template Strings

Permet de définir des templates sous la forme de chaîne de caractères sans passer par une librairie externe (mustache, jsrender, …)

La définition d’un string template se fait via un ` (back tick). Les variables sont définies via ${ x } (x étant la variable).
Les templates string peuvent être définis sur plusieurs lignes (sans devoir faire de concaténation comme avec les strings).

let person = {name: 'Julien Roy'};
let tpl = `My name is ${person.name}.`;

console.log(tpl); //My name is Julien Roy

Autres nouveautés

Harmony apporte également d’autres nouveautés:

  • les classes
  • les modules
  • Mutation observers

Je parlerai dans un autre article de ces nouveautés très attendues.

Edit : le deuxième article sur les nouveautés de ES6.

Utilisation

Bien entendu, toutes ses nouveautés ne sont pas encore toutes intégrées dans les navigateurs, sachant que les spécifications ne sont pas encore figées et peuvent être amenées à évoluer. Il existe tout de même des outils qui permettent d’utiliser ces fonctionnalités comme des polyfills ou des transpileurs (compilation de code ES6 en ES5) comme Traceur ou 6to5. (les liens ci dessous ..)

Conclusion

ES6 apporte pas mal de nouveautés attendues par les développeurs qui vont permettre à JavaScript de combler certains manques et améliorer grandement son écriture pour en faire un langage « first class ». Pour information, la version 2.0 d’Angular est écrite en EcmaScript 6.

Références

Protractor : Améliorer vos tests E2E avec le pattern Page Objects

J’ai déjà parlé dans des articles précédents de Protractor (ici et ), l’outil qui permet de faire des tests d’interface, end-to-end (E2E).

Je vais vous présenter ici Page Objects, un design pattern afin de faciliter le développement de vos tests. Page Objects permet :

  • d’avoir un code plus lisible
  • d’éviter la duplication de code
  • d’éviter d’avoir un couplage fort entre votre test et la page à tester
  • de faciliter la maintenance de vos tests

Une page object est une classe qui va servir d’interface entre la page à tester et vos tests. Ainsi, le test n’a pas à interagir avec votre interface mais passe par la classe « Page Object ». Ainsi, si votre interface change, seulement le code de votre page objet sera à modifier et non le test, facilitant ainsi la maintenance. La page object va exposer des méthodes fonctionnelles qui vont rendre plus lisible vos tests.

Exemple

Voici un exemple simple concernant une page de login.

Voici le test sans utilisation de page objects.

describe('Login', function () {

  var ptor, loginInput, passInput, submitBtn, error;

  beforeEach(function () {
    ptor = protractor.getInstance();
    loginInput = by.model('username');
    passInput = by.model('password');
    submitBtn = by.css('[type=submit]');
    error = by.binding('error');
  });

  it('should display error if login password are wrong', function () {
    element(loginInput).sendKeys('wrongUser');
    element(passInput).sendKeys('pass123');
    element(submitBtn).click();

    browser.waitForAngular();

    expect(ptor.getCurrentUrl()).not.toMatch(/\/home/);
    expect(element(error).getText()).not.toEqual('');
  });

  it('should redirect to home page is login/pass are correct',  function () {
    element(loginInput).sendKeys('admin');
    element(passInput).sendKeys('123456');
    element(submitBtn).click();

    browser.waitForAngular();

    expect(ptor.getCurrentUrl()).toMatch(/\/home/);;
  });
});

Voici comment améliorer nos tests avec Page Object. Dans le cas de protractor, les tests sont exécutés via node js. Nous allons créer un module node qui pourra ainsi être chargés dans nos tests.

La page Object de la page de login :

'use strict';

var LoginPage = function () {
  browser.get('http://mySiteUrl');
};

LoginPage.prototype = Object.create({}, {
    loginText: { get: function () { return element(by.model('username')); }},
    passwordText: { get: function () { return element(by.model('password')); }},
    loginButton: { get: function () { return element(by.css('input[type="submit"]')); }},
    error = { get: function () { return element(by.binding('error')).getText(); }},
    login: { value: function (login, password) {
    	this.loginText.sendKeys(login);
    	this.passwordText.sendKeys(password);
    	this.loginButton.click();

        browser.waitForAngular();
  }}
});

module.exports = LoginPage;

Le test devient :

//On charge le module
var LoginPage = require('../pages/login.page.js');

describe('Login', function () {
var page, ptor;

  beforeEach(function () {
    //avant chaque test on on récupère la page
    page = new LoginPage();
    ptor = protractor.getInstance();
  });

  it('should display error if login password are wrong', function () {
     page.login('wrongUser', 'pass');

     expect(ptor.getCurrentUrl()).not.toMatch(/\/home/);
     expect(page.error).not.toEqual('');
  });

  it('should redirect to project home page is login/pass are correct',   function () {
    page.login('julien', 'azerty123');

    expect(ptor.getCurrentUrl()).toMatch(/\/home/);
  });
});

On peut voir que le test est beaucoup plus lisible et maintenable.

De plus, il devient facile de réutiliser notre code. En effet, si on doit tester des pages ou l’on doit être identifié, on peut facilement réutiliser la page objects de login afin de se connecter avant chaque test.

J’ai mis à jour les tests E2E de mon application de volley. Le code est disponible sur github.

Références

Bon tests 😉

AngularJS : Lancer protractor avec Grunt

Dans un article précédent, je vous ai montré comment faire des tests end to end (E2E) avec Protractor pour tester une application Angular. Voyons comment nous pouvons utiliser grunt afin de faciliter le lancement de ces tests.

Nous allons avoir besoin des modules grunt à installer avec la commande :

npm install grunt-protractor-runner grunt-shell grunt-protractor-webdriver --save-dev
  • grunt-protractor-runner : permet de lancer protractor
  • grunt-shell : permet de lancer des commandes shells. Dans notre cas, il permet de mettre à jour webdriver dans la dernière version
  • grunt-protractor-webdriver : permet de lancer webdriver

Il faut ensuite modifier le fichier Gruntfile.js afin de configurer les plugins.

shell: {
  updateSeleniumWebDriver: {
    command: 'node node_modules/protractor/bin/webdriver-manager update',
      options: {
        stdout: true
      }
    }
  },

  'protractor_webdriver': {
     options: {
       // Task-specific options go here.
     },
     run: {
       // Target-specific file lists and/or options go here.
     }
  },

  protractor: {
    options: {
      configFile: 'node_modules/protractor/referenceConf.js', // Default config file
      keepAlive: true, // If false, the grunt process stops when the test fails.
      noColor: false, // If true, protractor will not use colors in its output.
      args: {
        // Arguments passed to the command
      }
    },
    local: {
      options: {
        configFile: 'protractor.conf.js', // Target-specific config file
           args: {} // Target-specific arguments
        }
      }
    }
  }

  ///autre configs ...

On créé ensuite une tache qui permet de lancer les tests E2E.

grunt.registerTask('e2e', [
    'connect:dist',
    'shell:updateSeleniumWebDriver',
    'protractor_webdriver:run',
    'protractor:local'
  ]);

La tache effectue les actions suivantes :

  • on lance le site avec connect
  • on met à jour selenium web driver
  • on lance webdriver
  • on lance les tests

Le lancement des tests se fait ensuite via la commande :

grunt e2e

Voici le résultat de l’exécution des tests :
Protractor lancé via grunt

Comme d’habitude, je l’ai mis en place sur mon application de Volley dont vous pouvez trouver le code sur github.

Pour ceux qui utilisent gulp, il existe les modules également :
gulp-protractror,
gulp-shell

AngularJs : Tester une directive avec un templateUrl

En AngularJS, lorsque le template d’un directive est complexe, une bonne pratique est de mettre ce template dans un fichier html séparé. Cela facilite la lecture de ce dernier et évite d’avoir à faire de la concaténation de chaines …

Cependant, lors de nos tests, le template n’est pas chargé et nous obtenons l’erreur suivante indiquant qu’une requête inattendue vers notre template à été effectuée.

Angular-Error-templateUrl

Voyons comment remédier à ce problème.

Il faut savoir que les templates sont ne sont chargés qu’une seule fois avant d’être mis dans un cache : le $templateCache. A la 2eme utilisation, le template sera récupérer dans le cache. Angular permet d’interagir avec ce templateCache. Ainsi, la méthode get permet de récuperer un template et put d’en rajouter un.

Nous allons pouvoir utiliser ce $templateCache dans nos tests afin d’y placer nos templates et pouvoir les utiliser lors de l’exécution des tests sans faire de requêtes http.

Nous allons utiliser pour le mettre en place le plugin grunt karma-ng-html2js-preprocessor. Ce dernier compile les template html en js, les enregistre dans le $templateCache et les enregistre dans un module qui nous permet de récuperer notre template.

L’installation du plugin se fait via la commande :

npm install karma-ng-html2js-preprocessor --save--dev

Il faut ensuite charger et configurer le plugin.

Voici le fichier karma.conf.js avec la configuration liée à karma-ng-html2js-preprocessor.


files: [
      //load js files ...
      
      //load directives templates
      'app/scripts/directives/templates/*.html'
    ],

// Plugins
plugins: [ 
 'karma-jasmine',
 'karma-chrome-launcher',
 'karma-firefox-launcher',
 'karma-coverage',
 'karma-ng-html2js-preprocessor'
],

ngHtml2JsPreprocessor: {
  // strip this from the file path
  stripPrefix: 'app/'
},

preprocessors: {
 'app/scripts/directives/templates/*.html': 'ng-html2js'
},

Voici les choses à faire :

  • Ajouter dans la liste des fichiers (files) vos templates html
  • Ajouter karma-ng-html2js-preprocessor à la liste des plugins à charger
  • Configurer le plugin dans la partie ngHtml2JsPreprocessor. (Dans mon cas, il suffit de dire qu’on ignore le prefix app/ dans le chemin des fichiers).
  • On configure ng-html2js dans la partie preprocessors afin de lui dire quels fichiers il doit traiter

Voici le test d’un directive chargeant un template html.

'use strict';

describe('Directive: numselect', function () {

  // load the directive's module
  beforeEach(module('volleyApp'));
  // load the template
  beforeEach(module('views/directives/num-select.html'));

  var element,
    scope;

  beforeEach(inject(function ($rootScope) {
    scope = $rootScope.$new();
    scope.options = 5;
  }));

  it('should create select with 4 options', inject(function ($compile) {
    element = angular.element('<numselect nb-options="options"></numselect>');
    element = $compile(element)(scope);

    scope.$digest();

    expect(element.find('select').length).toBe(1);
    expect(element.isolateScope().num.length).toBe(6);
  }));

  ///autres tests
});

Vous pouvez voir qu’on charge le template en tant que module.

J’ai mis en place ce mécanisme sur mon application de Volley. Voici le lien vers

Angular : Les propriétés spéciales dans un ngRepeat

Dans une boucle ngRepeat, chaque item à son propre scope. Les propriétés spéciales suivantes sont ajoutées dans le scope de ces derniers :

  • $index : la position de l’item (commence à 0)
  • $first : indique si c’est le premier élément
  • $middle : indique si c’est un élément entre le premier et le dernier
  • $last : indique si c’est le dernier élément
  • $even : indique si c’est un élément pair
  • $odd : indique si c’est un élément impair

On peut ainsi binder sur ces propriétés pour facilement afficher un index, affecter des classes CSS via ngClass …

Dans l’exemple suivant, on affiche l’index de l’élément (oui j’aurai pu utiliser une liste ordonnée 🙂 mais c’est pour la démo …) et on affecte une classe si l’élément est pair et n’est ni le premier ni le dernier.

La documentation officielle de ngRepeat

Angular : Test End to End avec Protractor

Je vous ai déjà parlé des tests unitaires sur un projet Angular. Aujourd’hui je vais vous présenter comment faire des tests d’intégration (end to end ou E2E). Le but des tests d’intégration est de s’assurer que tous les composants de votre application fonctionnent correctement ensemble. Pour cela, les tests seront joués dans un navigateur et des actions utilisateurs seront simulées.
Les tests d’intégration sont donc complémentaire des tests unitaires.

Angular a été pensé pour être testable et il y a donc un outil pour les tests E2E : Protractor, développé par la team Angular (Il est possible de l’utiliser pour tester des applications qui ne sont pas basé sur Angular).
Protractor utilise Selenium qui permet de contrôler un navigateur (aller à une page, cliquer sur un bouton, …)

Installation

Protractor est un module node et il faut donc avoir node installé sur son poste.

Je vous conseille de l’installer en global (Préfixer la commande par sudo si vous êtes sous Linux (Ubuntu pour moi).

npm install protractor -g

Il faut ensuite configurer selenium via la commande suivante

webdriver-manager update

Configuration

La configuration de Protractor se fait via un fichier .js.
Voici celui que j’utilise dans mon application de volley :

Ecriture des tests

Il faut ensuite écrire vos tests. Par défaut, protractor utilise Jasmine mais vous pouvez utiliser d’autres framework de test comme mocha par exemple.

Protractor met à votre disposition des variables globales vous permettant d’interagir avec le navigateur comme par exemple :

  • browser: browser.get(‘url’) : permet d’ouvrir la page passé en paramètre
  • element and by: element(by.model(‘yourName’)) : permet de récuperer un élement de la apge
  • protractor: protractor.Key : permet la gestion des touches (ex : protractor.Key.ENTER)

Protractor étant à la base pensé pour angular, il fournit des sélecteurs lié à angular comme

  • by.model(‘first’) : récupère l’input lié à la variable de scope first (ng-model= »first »)
  • by.binding(‘myModel’) : récupere l’élement bindé sur myModel (
  • by.repeater(‘user in users’).row(0).column(‘name’) : récupère le nom d’un 1er element de la liste users

Voici une exemple de test sur mon application de volley qui permet de vérifier qu’on ne peut pas lancer de partie si le nom d’une des équipes n’est pas renseignée :

Voici le lien vers la documentation officielle de l’api de protractor ainsi qu’une CheatSheet et un gist (en coffeescript).

Dans le cas ou vous faites un appel serveur, il faut utiliser la ligne suivante afin de ne pas poursuivre vos tests et notamment vos vérifications tant qu’angular n’a pas traité le retour

browser.waitForAngular();

Lancer les tests

Il faut d’abord lancer le server Selenium (webdriver) via la commande suivante :

webdriver-manager start

On peut alors lancer dans un autre terminal protractor :

protractor test/e2e/config.js // Chemin vers votre fichier de configuration

Une fenêtre de navigateur va alors se lancer et jouer les tests sous vos yeux. Magique !

Le résultat de vos tests sera affiché dans la console :

Protractor : résultats des tests E2E

Comme vous pouvez le voir, il me reste encore du travail pour tout valider 😉 .

Attention, votre site doit être accessible. Dans mon cas, je le lance via la commande

grunt serve

NB : Il existe un plugin pour lancer protractor depuis grunt : grunt-protractor-runner. Je n’ai pas encore eu le temps de le tester …

Ressources