Archives du mot-clé tests unitaires

.NET : Génération de jeu de données avec Nbuilder et Faker.Net

Nous avons parfois besoin de jeu de données factices pour nos tests unitaires, peupler une base de données ou pour simuler un service externe par exemple. Il peut être complexe et assez lourd de créer des jeu de données. Je vais vous présenter NBuilder et Faker.Net qui sont 2 librairies pour vous aider à générer vos jeux de données. Ces 2 librairies sont disponibles sous forme de package NuGet.

NBuilder

NBuilder est une librairie qui permet de créer facilement et rapidement des objets, basé sur le design pattern builder. Il va renseigner automatiquement pour vous les propriétés publiques de vos objets. Vous pouvez bien entendu préciser comment générer certaines propriétés.
Par défaut, NBuilder crée des valeur séquentielle dans le cas des liste (Name1, Name2, …)
NBuilder fournit une classe GetRandom qui permet de générer des données aléatoire pour un certains type de données (url, nom , pays, …).

//Création d'un objet
var product = Builder<Product>
    .CreateNew()
        .With(x => x.Name = "Nexus 5")	
	.With(x => x.Price = 199)
    .Build();

//Création d'une liste
var products = Builder<Product>
  .CreateListOfSize(10)
  .All()
  .With(x => x.Name = GetRandom.String(10))	
  .With(x => x.Price = GetRandom.Int(100, 400))
  .Build();

NBuilder fournit également Pick qui permet de sélectionner un objet dans une autre liste

//Sélection d'1 seul élément
Pick<Category>.RandomItemFrom(categories))
//Sélection d'un sous ensemble d'élément
Pick<Category>.UniqueRandomList(With.Between(5).And(10).Elements).From(categories);

Faker.NET

Faker.Net est un bon complément à NBuilder car il permet de générer des valeurs plus réaliste pour les données. Faker.Net contient plus de type de données que NBuilder.

Il contient des générateurs pour:

  • Addresse: (pays, code postal, ville, …)
  • Téléphone
  • Internet (email, password, IP, …)
  • Nom
  • Date
  • Entreprise
  • Finance (Prix, Compte, …)
//Utilisation de Faker.Net avec NBuilder
var customers = Builder<Customer>.CreateListOfSize(100)
        .All()
            .With(c => c.FirstName = Faker.Name.First())
            .With(c => c.LastName = Faker.Name.Last())
            .With(c => c.EmailAddress = Faker.Internet.Email())
            .With(c => c.TelephoneNumber = Faker.Phone.Number())
        .Build();

Un petit exemple d’utilisation avec .Net Fiddle

Liens

Angular : Directive avec controller as, bindToController et tests

Une des bonnes pratiques promue par les experts angular est l’utilisation des controller en utilisant la syntaxe ‘controller as’ (des liens sur le sujet en bas de l’article).
En effet, cette pratique permet d’éviter certains écueils lié au prototypage JavaScript et permet de ne plus se coupler au scope.
La notion de scope n’existera plus dans angular 2 (de même que les controller d’ailleurs …) et il est donc conseillé de limiter son utilisation afin de faciliter une migration.

Les équipes d’angular travaillent sur les versions 1.x afin de faciliter la migration et contiennent donc des améliorations pour cela.

La version 1.3 a introduit la propriété bindToController qui permet d’automatiquement de rendre disponible les propriétés déclarés sur le scope dans le controller d’une directive.

La version 1.4 permet de définir directement sur bindToController les propriétés comme on le faisait sur le scope.

Directive avec controller as

Voici un exemple d’une directive tirée de mon application de volley ou l’on déclare une directive avec un controller en utilisant la syntaxe controller as et bindToDirective.

function matchScore() {
  return {
    templateUrl : 'views/directives/match-score.html',
    restrict : 'E',
    scope : true,
    bindToController : {
        match : '='
    },
    controllerAs : 'matchScore',
    controller : function () {
      return {
        scoreSets : function (set, team) {
          return this.match.score[set]
	    .filter(val => val === team)
            .length;
        }
      }
    }
  }
}

angular.module('volleyApp').directive('matchScore', matchScore());

Dans le vue de notre directive, il faut alors utiliser matchScore comme préfixe pour nos bindings.

<div class="list-group-item-text">
    <table class="table table-bordered score">
    <tbody>
    <tr>
        <th>Team</th>
        <th>Set 1</th>
        <th>Set 2</th>
        <th>Set 3</th>
        <th>Set 4</th>
        <th>Set 5</th>
    </tr>
    <tr id="scoreTeam1">
        <th><span id="team1Name">{{matchScore.match.teams.team1}}</span></th>
        <td class="set1"><span>{{matchScore.scoreSets(0, 1)}}</span></td>
        <td class="set2"><span>{{matchScore.scoreSets(1, 1)}}</span></td>
        <td class="set3"><span>{{matchScore.scoreSets(2, 1)}}</span></td>
        <td class="set4"><span>{{matchScore.scoreSets(3, 1)}}</span></td>
        <td class="set5"><span>{{matchScore.scoreSets(4, 1)}}</span></td>
    </tr>
    <tr id="scoreTeam2">
        <th><span id="team1Name"></span>{{matchScore.match.teams.team2}}</th>
        <td class="set1"><span>{{matchScore.scoreSets(0, 2)}}</span></td>
        <td class="set2"><span>{{matchScore.scoreSets(1, 2)}}</span></td>
        <td class="set3"><span>{{matchScore.scoreSets(2, 2)}}</span></td>
        <td class="set4"><span>{{matchScore.scoreSets(3, 2)}}</span></td>
        <td class="set5"><span>{{matchScore.scoreSets(4, 2)}}</span></td>
    </tr>
    </tbody>
</table>

Tests

Si on utilise pas controller as, les méthodes du controller sont accessible via le scope.

Si on utilise controllerAs, nous n’avons plus de scope pour accéder aux méthodes de notre controller. Comment alors tester nos méthodes de controller ?

Angular fournit des méthodes additionnelles à element (élément jQuery/jqLite) et fournit notamment une méthode element.controller qui permet de récupérer un controller. Il faut l’appeler avec le nom de la directive (et non par celui définit via controller as).

//on récupère le controller via element 
var ctrl = element.controller('matchScore');

//On teste nos méthodes
expect(ctrl.scoreSets(0, 1)).toBe(25);

Liens

.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 🙂 …

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 : 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

AngularJs : Quelques astuces pour les tests unitaires

Une des grandes forces d’Angular est qu’il a été développé pour être facilement testable. Voici quelques astuces pour mieux tester vos applications angular.

Nommage

Dans un fichier de test, nous allons avoir besoin d’utiliser plusieurs fois des objets (services, controllers, …) dans plusieurs tests. Une bonne pratique pour avoir moins de code est d’injecter les différents objets avant chaque test dans le beforeEach global de notre fichier. Comme angular va se charger d’injecter nos objets via leur nom, il n’est pas possible de nommer ses variables du nom de l’objet. Il existe une astuce qui consiste à entourer le nom du service par des underscore (par exemple _user_)

// Defined out reference variable outside
var myService;

// Wrap the parameter in underscores
beforeEach( inject( function(_myService_){
  myService = _myService_;
}));

// Use myService in a series of tests.
it('makes use of myService', function() {
  myService.doStuff();
});

Le lien vers la documentation d’Angular concernant ce point

Organisation de vos tests

Un fichier de tests avec Jasmine est organisé en blocs (describe) qui contiennent des sous blocs et des tests (it). Un bloc describe peut également contenir une fonction beforeEach qui permet d’initialiser des objets avant un test. N’hésitez pas à faire plusieurs niveaux de describe ayant chacun un beforeEach afin de simplifier votre fichier de tests

describe('Mon fichier à tester', function() {
  // Chargement du module à tester
  beforeEach(module('myWebApp'));

  var monCtrl,
      scope,
      myService;

  // Init global ...
  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope, _myService_) {
    scope = $rootScope.$new();
    myService = _myService_;

    monCtrl = $controller('MonCtrl', {
      $scope: scope
    });
  });

  describe('Mon fonction à tester', function() {
    beforeEach(function() {
      //init lié à ma fonction (création var, spy, ...)
    });
    
    it('test 1', function () {
        ...
    });

    it('test 2', function () {
        ...
    });

  });

  describe('Mon 2eme fonction à tester', function() {
    beforeEach(function() {
      //init lié à ma fonction (création var, spy, ...)
    });

    it('test 3', function () {
        ...
    });

  });

});

Simuler un serveur web

Pour tester vos fonctions qui ont besoins de retour du serveur, angular founit l’objet $httpBackend qui permet de mocker des réponses à des requêtes http.

Voici comment par exemple simuler le retour d’un appel serveur qui renvoie un user

$httpBackend.when('GET', '/user/1').respond({userId: 1, firstname : 'Julien', lastname : 'Roy'});

Le lien vers la documentation de $httpBackend.

Tester des promises

Angular inclut $q, un service de promise, qui permet de facilement gérer enchaînement de méthodes asynchrones.

Voyons comment tester une méthode appelant une méthode retournant une promise.

var deferredSuccess = q.defer();

spyOn(projectFactory, 'getPotentialOwnersForProject')
   .and.returnValue(deferredSuccess.promise);

deferredSuccess.resolve(usersMock);
//Il faut appeler digest pour que le resolve soit pris en compte.
scope.$digest(); //ou scope.$apply();

Filtrer les tests à exécuter

Lors de l’écrire de vos tests, il se peut que vous n’ayez pas envie de jouer tous les tests de votre application pour corriger un test ou une série de tests (bloc describe contenant 1 ou plusieurs test it). Il est possible avec Jasmine de filtrer et d’exclure des tests :

  • Ne jouer qu’un seul test : changer it en iit
  • Ne pas jouer un test : changer it en xit
  • Jouer un bloc describe : changer describe en ddescribe
  • Ne pas jouer un bloc describe :changer describe en xdescribe

Edit 14/06/2015 : à partir la version 2.1 de Jasmine, il faut utiliser respectivement fit et fdescribe au lieu de iit et ddescribe (http://jasmine.github.io/2.1/focused_specs.html).

Ecrire dans la console des tests

Si vous souhaitez écrire dans la console des tests (à des fins de debug par exemple), il faut utiliser la méthode dump(var) au lieu de console.log(var) comme on pourrait le penser.

Directive et isolate scope

Dans le cas de directive avec un isolate scope, le moyen d’accéder au scope de la directive se fait via :

element.isolateScope()

Liens

Ajouter la couverture de code dans un projet Angular avec Karma

Certains d’entre vous le savent peut être si vous me suivez sur Twitter, je suis en train de porter mon application de gestion de score de volley (qui utilise knockout JS) sous Angular JS (avec en prime de nouvelles fonctionnalités 🙂 .

J’utilise yeoman pour la gestion de mon application via le générateur pour angular.

Par défaut, le générateur utilise Karma pour la gestion des tests unitaires. Karma permet de tester votre code dans différents navigateurs.

Par défaut, vous n’avez pas d’information concernant la couverture de code des tests, métrique très importante afin de savoir quelle partie de votre code est couvert par les tests ou celle qui ne l’est pas. Voyons comment l’ajouter.

Mise en place de la couverture de code

La gestion de la couverture de code utilise le module karma-coverage (qui utilise le module istanbul) à installer via la commande (l’option –save-dev permet de rajouter le module en tant que dépendance de développement (devDependencies) dans le fichier package.json de votre application) :

npm install karma-coverage --save-dev

Il faut ensuite ajouter les lignes suivantes dans le fichier karma.conf.js :

//Code Coverage
reporters: ['progress', 'coverage'],
preprocessors: { 'app/scripts/**/*.js': ['coverage'] }

C’est tout !!

Utilisation

Le lancement de vos tests unitaires se fait toujours via la commande :

grunt test

Un dossier coverage est créé dans votre arborescence et contient un dossier par navigateur dans lequel vous avez testé votre application.
Ajout du dossier coverage
Dans ces dossier vous avez un fichier index.html. Lancez ce fichier dans votre navigateur et vous avez maintenant accès à la couverture de code de votre application. Vous naviguer dans l’arborescence de votre projet et consulter fichier par fichier la couverture de code.

Couverture de code de votre application

Couverture de code d'un fichier

Comme vous pouvez le voir, la couverture n’est pas complète mais j’y travaille 😉 .

Bonus : Pensez à rajouter le dossier coverage dans le fichier .gitignore.