Bienvenue sur Développement Agile

Référence sur le développement logiciel Agile. Nous traitons de conception, de programmation, de pratiques de génie logiciel, d'essais et d'autres sujets connexes.

Présentation: Propulsez votre architectures grâce au TDD et aux Mocks (Agile Montréal 2014)

Publié par Félix-Antoine Bourbonnais le mercredi 12 mars 2014 à 06:00

Cette session a été présentée par Félix-Antoine Bourbonnais à la conférence mensuelle d'Agile Montréal le 12 mars 2014.

Description

Nous savons depuis longtemps que les tests automatisés jouent un rôle important pour les équipes de développement Agile. Bien que la communauté ait découvert depuis un certain temps des pratiques permettant de maximiser l’émergence du design via le TDD, il est rare que l’on présente des astuces concrètes pour obtenir ce bénéfice.

Cette présentation explique comment tirer le maximum de vos tests unitaires et des « mocks ». Nous présenterons, plus particulièrement, le style de TDD « mockiste ». Ainsi, nous verrons comment les mocks peuvent nous aider à concevoir une architecture ayant une meilleure conception orientée objet. 

  • Niveau : Avancé
  • Public cible : Développeurs et architectes

Présentation

 

Diapositives (PDF) 

Code source

 

Le code source de la démonstration est disponible pour téléchargement:
https://github.com/fbourbonnais/propulsez-architecture-tdd-mocks

 

Autres billets

 

Test unitaire: la définition qui lève les barrières

Publié par Félix-Antoine Bourbonnais le lundi 10 février 2014 à 00:00

Beaucoup d’entreprises où nous passons semblent avoir une définition des tests unitaires qui seraient mieux résumés, selon moi, par le terme “tests développeurs”.

On considère alors, à tort, un test unitaire comme étant un test effectué par les développeurs avant les tests “fonctionnels”. On les associe aussi souvent, de manière erroné, à un test des limites sur chacun des champs d’un écran.

Ce n’est pas l’unité dont on parle ici ! L’unité dans un test unitaire est la classe (le code) ! C’est donc un test d’un niveau bien plus bas qui n’impacte que le développeur...

Peu importe le nom, la clé est de comprendre que grâce à la technologie moderne, il est désormais possible d’avoir des tests qui opèrent directement au niveau du code (chaque classe individuellement). Il s’agit alors d’une responsabilité propre aux développeurs indépendants des analystes fonctionnels ou de la gestion.

Ce niveau très microscopique qui nous est offert par la technologie s’avère également être celui qui est le moins fragile (probabilité qu’un test soit impacté par beaucoup de changements dans le système) car il inspecte une zone très limitée (portée).

Voici la clé de la maintenabilité des tests...

Démystifier les types de tests

Publié par Félix-Antoine Bourbonnais, Pascal Roy le vendredi 24 janvier 2014 à 00:00
Taxonomie-icone


L’expérience nous a permis de constater qu’il semble y avoir une confusion assez répandue au sein de l’industrie quant à la terminologie et le rôle des différents tests utilisés dans le cadre du développement logiciel Agile.

La plupart des entreprises semblent avoir leur propre terminologie basée sur leur expérience antérieure à l’agilité. La littérature Web présente de multiples versions souvent très disparates des mêmes concepts (e.g. pyramide des tests), ce qui vient probablement ajouter à cette confusion.

Dans ce premier article, nous proposons d’explorer avec vous quels sont les différents types de tests et leurs objectifs de même que la portée des différents niveaux de tests.

 

Thèmes abordés dans cet article

  1. Type versus niveau de tests
  2. Est-ce qu'un type peut être écrit à plusieurs niveaux (ex.: un test d'acceptation unitaire)?
  3. Types de tests (selon l'objectif): Acceptation, Fonctionnel, Performance ...
  4. Niveaux de tests (selon la portée): Bout-en-bout, Système, Intégration, Composante, Unitaire
  5. Autres taxonomies: Boîte noire et blanche, Développeurs VS utilisateur, UI, Validation et vérification (V&V) ..

 

Lire l'article complet

Jasmine partie 3 : Gestion D'événements Asynchrones

Publié par Jean-Nicolas Viens le jeudi 25 juillet 2013 à 07:46

Ceci est le dernier, mais non le moindre, d’une série de trois articles à propos de Jasmine. Je vous invite à regarder d’abord le premier et le deuxième article avant celui-ci. Nous continuerons avec le concept de mocks vu lors de la deuxième partie afin de pouvoir contrôler le temps! Cela nous permettera, entre autres, de tester des comportements répétés (à toutes les X secondes), ou encore d’accélerer des effets graphiques pour améliorer les performances de nos tests.

Tester des comportements asynchrones

Il existe plusieurs cas de figures où l’on veut attendre la fin d’une action avant d’en valider le résultat. Par exemple, on peut vouloir attendre la fin d’un appel AJAX avant de s’assurer que le contenu d’un conteneur a été changé, ou qu’une fonction a été appelée. Cependant, nous allons profiter de ces exemples pour introduire une extension à Jasmine : jasmine-jquery. Celle-ci nous permet d’utiliser la syntaxe expect($object_jquery), en plus de plusieurs nouvelles méthodes pour valider le contenu de cet objet jQuery. Par exemple, supposons que nous avons un <div> avec fenetre comme id :

expect($("#fenetre")).toBeVisible();

Vous trouverez toutes les informations pertinentes sur ces nouvelles méthodes sur la page github de cette extension. Nous n’élaborerons pas sur ce sujet ici, mais sachez que cette extension permet également l’utilisation de fixtures HTML, de même que l’utilisation de mocks pour les événements.

Illustrons la valeur de jasmine-query et des mocks dans un cas simple mais fort typique en développement web. On veut s’assurer que lorsque le bouton “fermer” de notre fenêtre est appelé, alors la fenêtre se ferme et une autre s’ouvre. Comment vérifier cela? Simplement en vérifiant si la fenêtre #2 est maintenant visible après la fermeture de la fenêtre #1. Si la fenêtre se ferme instantanément, ce n’est peut-être pas un problème, mais qu’arrive-t-il si la fenêtre se ferme avec une jolie animation? Et bien, tout au long de l’animation elle sera visible et notre test échouera!

Jasmine nous permet de séquencer des blocs de code avec la méthode runs(). Ceux-ci seront simplement exécutés dans leur entièreté les uns après les autres dans l’ordre. On peut également ajouter un bloc waitsFor() qui sera exécuté tant et aussi longtemps que celui-ci ne retournera pas vrai ou qu’un timeout ne sera pas atteint. Un bloc runs() ne sera jamais executé tant et aussi longtemps que le bloc waitsFor() qui le précède ne retournera pas true. Tout cela est certainement plus clair avec un exemple :

Notez la structure de ce dernier exemple:

  1. Un bloc de code runs() qui se termine par un appel asynchrone (en jQuery, la fonction animate retourne avant d’avoir complété l’animation en question)
  2. Un bloc de code waitsFor() qui exprime une condition à attendre (note: le timeout est optionnel)
  3. Un bloc de code runs() avec une assertion

Le seul problème de cette technique est que le test est très verbeux, il y a beaucoup de bruit pour rien. Le problème peut être mitigé par l’utilisation d’un langage tel le coffeescript. Voici le même exemple, écrit en coffeescript :

Cependant, il est à noter que ce genre de test n’est pas souvent nécessaire. Dans ce cas particulier, il aurait été préférable de désactiver toutes les animations jQuery tout simplement. On peut faire cela avec cette ligne de code :

jQuery.fx.off = true;

Ce qui a pour effet que toutes les animations sont instantanées.

Bien qu’il est pratique de pouvoir faire tout cela, il est généralement souhaitable de ne pas (ou peu) se coupler du DOM lors de tests Javascript, mais cela sort de la portée de cet article.

Contrôler le temps

Jasmine nous permet également de contrôler le temps en utilisant des mocks sur les fonctions setTimeout et setInterval du Javascript. Heureusement, cette complexité est encapsulée pour nous. Nous avons simplement à dire à Jasmine d’utiliser un mock pour le temps, ce qui fait qu’aucune fonction passée à setTimeout et setInterval ne sera executée tant qu’on ne l’aura pas demandé. Le temps sera “gelé” et nous pourrons alors avancer le temps X secondes à la fois, selon les besoins du test. Pour dire à Jasmine d’utiliser un mock pour gérer le temps, on utilise :

jasmine.Clock.useMock();

Cela installe le mock et s’assure de le désinstaller après chaque test. On peut donc l’utiliser sans danger de collision avec un autre test. On peut également l’utiliser dans le beforeEach(). Par la suite, on utilise la fonction tick(<millisecondes>) pour faire avancer le temps. Voici un exemple typique d’un test :

jasmine.Clock.useMock();
// -> action qui utilise le setTimeout ou setInterval
jasmine.Clock.tick("nombre en millisecondes")
// -> validation que l'action a été exécutée

Il est également possible d’utiliser d’autres fonctions de Clock, dont voici les plus utilisées :

  • reset : retourne le compteur à 0
  • installMock : comme useMock(), mais ne désinstalle pas automatiquement le mock après le test. Il est généralement conseillé d’utiliser useMock().
  • uninstallMock : retire le mock manuellement. Attention, si vous avez utilisé useMock(), vous risquez d’avoir une exception vu que uninstallMock() sera appelé automatiquement une 2e fois.
  • isInstalled : retourne vrai ou faux, selon que le mock pour le temps est installé
  • assertInstalled : même chose, mais lance une exception de type Error

Voici un exemple complet, avec une fonction qui valide l’état d’une session d’un usager à chaque seconde.

Il est possible de faire exécuter instantanément des animations jQuery avec cette technique, mais cela requiert de placer ce bout de code avant de charger jQuery :

window.webkitRequestAnimationFrame = null;
window.mozRequestAnimationFrame = null;
window.oRequestAnimationFrame = null;
window.requestAnimationFrame = null;

Ce code a pour but de désactiver la gestion de “frames” dans le navigateur puisque jQuery utilise cette fonction plutôt que setTimeout désormais. On peut par la suite exécuter pas-à-pas une animation.

Il est pertinent de noter aussi que l’extension jasmine-ajax utilise le même principe pour détourner les appels AJAX, soit :

jasmine.Ajax.useMock();

Je n’entrerai pas dans les détails de cette extension, mais elle vaut certainement le coup d’oeil!

Conclusion

Bien que nous ayons vu une grande partie des fonctionnalités qu’offre Jasmine, plusieurs questions restent. Il y a beaucoup de facteurs à prendre en compte lors de la conception de tests Javascript, principalement : quoi tester? Il faut éviter de trop se coupler sur le DOM et se concentrer sur la logique d’affaires qui se retrouve du côté du client. Il y a aussi plusieurs pièges à éviter, et tout cela fera vraisemblablement partie d’un prochain article sur les tests Javascript. Il faut mentionner également que mon collègue Vincent a produit un article de même nature que celui-ci sur qUnit, outil de tests offert par les développeurs de jQuery.

Cette série d’articles à propos de Jasmine est terminée en soi, mais il reste un sujet à traiter : l’intégration avec nos outils. Dans un prochain article, vous pourrez voir comment intégrer Jasmine.js à Visual Studio, à votre build dans TFS et à une application Rails.

Jasmine partie 2 : Les mocks

Publié par Jean-Nicolas Viens le mardi 23 juillet 2013 à 14:26

Pour faire suite à l’article précédent à propos de Jasmine, voici un nouvel aspect de cet outil : les mocks. Ceux-ci nous permettent de tester un composant de notre code en isolation. On peut ainsi valider l’interaction avec un autre composant ou forcer une réaction de la part de ce composant. Tout cela avec la syntaxe fluente de Jasmine :

spyOn(conversion, "livreEnKilo").andReturn(10);

Utilisation des mocks

Vocabulaire : Jasmine offre la méthode spyOn(); et utilise le terme “espion” pour les objets ainsi crées. Cependant, ces objets sont selon moi des mocks en réalité. J’utiliserai donc le terme mock dans cet article. Martin Fowler a un article sur les différences entre les stubs et les mocks qui est très complet. Il y a aussi cette réponse sur stackoverflow qui résume bien les différences.

Utilisant la flexibilité d’un langage dynamiquement typé tel le Javascript, Jasmine nous propose une façon simple et élégante de créer des mocks. Voici un exemple :

spyOn(objet, "méthode").comportement()

On utilise la méthode spyOn en lui passant l’instance de l’objet à “mocker” ainsi que le nom dela méthode sous forme de string. On peut également associer un comportement à notre mock avec une de ces méthodes :

  • par défaut : la méthode est sans comportement et retourne toujours undefined.
  • andCallThrough() : Installer le mock, mais utiliser la méthode originale lors de l’appel
  • andReturn(<retour>) : Retourner cette valeur à l’appel de la méthode
  • andCallFake(<factice>) : appeler la méthode sur un second objet factice

Si vous voulez “mocker” une méthode globale (ce qui devrait être rare), rappelez-vous que tout objet global est en fait à l’intérieur de la variable window. Vous pouvez donc utiliser, par exemple, spyOn(window, "alert”); .

Maintenant, comment valider qu’il y a eu une interaction avec un mock? Avec la méthode expect(), comme pour les assertions! Il suffit de passer la fonction “mockée” en paramètre et utiliser soit toHaveBeenCalled() ou toHaveBeenCalledWith(<paramètres>). Comment cela fonctionne-t-il? Il faut simplement se rappeler que objet.action() appelle la fonction action() sur objet et retourne le résultat de celle-ci, alors que objet.action retourne la fonction action elle-même sans l’exécuter. Jasmine tire donc profit de cette fonctionnalité du Javascript pour nous permettre de faire :

spyOn(window, "alert");
alert("test");
expect(window.alert).toHaveBeenCalledWith("test");

Note : toHaveBeenCalledWith() valide également le type du paramètre, donc “10” (string) et 10 (integer) ne sont pas pareils ici.

Attention, il est fréquent de voir expect(window.alert()), ce qui ne fonctionne pas puisque le retour de la fonction alert n’est pas un mock!

On peut également valider seulement le type du paramètre :

spyOn(window, "alert");
alert("test");
expect(window.alert).toHaveBeenCalledWith(jasmine.any(String));

Note : Tout type ou constructeur est valide ici (Function, String et Number sont sûrement les plus utilisés)

Nous pouvons donc valider que le paramètre est exactement la valeur voulue ou du bon type, mais il est impossible de valider une condition sur ce paramètre. En fait, Jasmine n’offre pas cette fonctionnalité “out of the box”, mais l’outil est assez bien construit pour que ce soit trivial d’implémenter ce comportement. J’ai donc écrit ce plugin qui permet de valider un paramètre de la façon suivante :

expect(window.alert).toHaveBeenCalledWith(jasmine.Arg().contains("est"));

Nous ne nous y attarderons pas dans cet article, mais notez qu’il est également possible de créer un mock à la volée avec createSpy() et createSpyObj().

Pour terminer, voici une démonstration sur les mocks.

Cet exemple utilise également la méthode beforeEach(), dont l’utilité devrait être claire à ceux habitués aux outils de la famille xUnit. Cette méthode sera exécutée une fois avant chaque spécification. Notez que les beforeEach() sont cumulatifs lorsque l’on dispose de spécifications imbriquées. Bien entendu, il existe également afterEach().

Une astuce intéressante est de remarquer que tous les appels AJAX dans jQuery passent par la méthode $.ajax(), ce qui rend les choses faciles à tester. En voici un exemple :

Conclusion

En terminant, sachez que vous pouvez également utiliser Sinon.js, qui est un framework de mocks en Javascript. Celui-ci cohabite bien avec Jasmine, si vous avez une préférence pour Sinon.js.

Dans le troisième et dernier billet à propos de Jasmine, nous utiliserons les mocks dans un but étrange, mais puissant : contrôler le temps!

Lire le billet suivant

Archive