ngParis #10

Introduction

Premier meetup angular pour moi. Dans les locaux d'eNovance. On a parlé du centre d'appel pour AutoLib qui est fait avec Angular et on a ensuite parlé de tests avec Protractor.

Le centre d'appel AutoLib avec AngularJS

Présenté par Stéphane Raimbault

Le centre d'appel AutoLib est ouvert 24/7, avec 40 conseillers qui gèrent les appels, dans plusieurs villes de France. Le système a été développé par 20 devs, essentiellement en python (Django), avec un peu de JS pour la couche client.

Les devs ne faisaient pas de front au début, ils ont commencé à ajouter du jQuery, et ça marchait bien. Mais plus il y avait de feature à ajouter et plus les modifications leur coutait cher en temps de dev. Ils ne pouvaient pas tester unitairement leur code, donc ils ont investi dans des tests selenium pour s'assurer qu'il n'y avait pas de regression. Finalement, ils ont sauté le pas pour passer à Angular, et depuis ils prennent plaisir à développer du JS.

Sur le poste de chaque téléconseiller tourne un applet Java et un "soft phone". Dès qu'un appel arrive, son numero de téléphone est envoyé à l'applet, qui fait un lookup pour savoir à quel client et à quelle ville il correspond. L'applet lance ensuite un navigateur en spécifiant l'url à ouvrir (contenant l'id du client). La page web ainsi affichée est divisée en 3 parties.

  1. Destinée aux RH. Compte le temps des appels, le temps avant la prochaine pause, etc.
  2. Partie toujours affichée, avec un champ libre pour laisser des informations sur l'appel, mis à jour en temps réel, pas besoin de validation.
  3. Partie de navigation : itinéraire, solde, informations sur la voiture, etc.

Toutes les informations remplies dans les $scope sont automatiquement stockées dans le localStorage avec ngStorage. Ainsi, toutes les pages ouvertes pointent vers le même utilisateur (pour éviter au téléconseiller d'avoir plusieurs onglets sur des utilisateurs différents et les confondre). Attention, le localStorage est stocké sur le profil Chrome de l'utilisateur, qui peut être stocké sur le cloud, donc attention à la consommation en bande passante.

La partie RH et la partie client font appel à deux SI différents, sur deux domaines différents. Ils ont donc du gérer les soucis de CORS (Cross Origin Request Sharing). Pour cela, le serveur distant (nginx, apache) doit accepter le verbe OPTIONS. Chrome possède une politique de cache aggressive sur ce verbe, attention aux erreurs lors du debug. Le serveur doit aussi répondre le header Access-Control-Allow-Origin, et angular doit envoyer les credential BasicAuth avec $httpProvider.defaults.withCredentials = true

Pour faire leurs tests, ils générent des grappes de jeux de données avec FactoryBoy (en python) qui leur créé des utilisateurs avec des contracts, des voitures, etc, dans à peu près tous les états possibles. Ils mélangent les tests Selenium avec des tests plus techniques (ex. vérifier qu'une donnée est bien modifiée en DB après une interaction dans le browser).

A posteriori, ils sont contents d'avoir switché à Angular mais ils regrettent de devoir dupliquer de la logique (vue/controlleur codé en front doit aussi être codé en back), le support de l'i18n en Angular est assez limité. Ils sont par contre très content d'avoir quitté jQuery, et leurs devs aiment désormais faire du JS.

Protractor

Protractor est un outil qui permet de faire de tests fonctionnels en Angular (aussi appellés tests end to end, test d'intégration web, tests de gui, acceptance tests, etc). Ils permettent de tester toutes les couches de l'appli, depuis le GUI jusqu'aux interactions avec la DB.

Il se base sur Selenium, qui lui-même utilise l'API WebDriver pour "parler" aux différents browsers. Initialement Selenium utilisait une solution full-js, mais cela limitait les interactions, désormais WebDriver communique avec chaque browser dans une syntaxe qui lui est propre mais abstrait ça sous une couche commune. Protractor utilise l'implémentation nodeJS de WebDriver, nommée sans grande surprise WebDriverJS (il en existe aussi en ruby, java, etc).

Protractor est parfaitement lié avec Angular, il sait donc quand un appel est en cours, quand il est revenu, ou quand un cycle de digest est en cours et attends donc qu'ils soient terminés pour continuer son execution. Ca évite de devoir mettre des sleep() partout. Il permet bien sur de selectionner des éléments à partir de sélecteurs CSS, mais aussi à partir de selecteurs propres à Angular (comme sur le nom des bindings, des models, des controlleurs, etc). Pour finir, il s'intégre avec SauceLabs.

ngScenario est l'ancetre de protractor, aujourd'hui deprecated, qui pilotait l'appli en full js, sans selenium. Nightwatch est une autre solution de tests e2e mais n'est pas spécifique à Angular. CasperJS quand à lui est uniquement pour les moteurs headless (phantom, slimer).

Protractor s'installe depuis npm. Il suffit ensuite de mettre à jour le driver selenium avec node ./node_modules/protractor/bin/webdriver-manager update et de lancer le serveur selenium avec start dans un terminal, puis de switcher dans un autre et lancer les tests protractor. Il existe bien sur une tache grunt pour faire ça automatiquement. Le fichier de config selenium indique l'url où tourne selenium, la liste des browsers à tester (avec potentiellement les arguments à passer pour les lancer, comme par exemple un profil spécifique) et l'url du site à tester.

Pour écrire des tests Protractor, on utilise essentiellement trois objets

  1. browser, et browser.driver qui est l'API webdriver, pour reload, charger des pages, resizer la page, etc
  2. element pour intéragir avec les éléments du DOM
  3. by pour faire des sélection XPath, ou selon les sélecteurs Angular

Globalement la doc est pourrie. Il vaut mieux regarder les fichiers de tests de Protractor directement pour voir comment l'utiliser. Pour le debug, on peut utiliser browser.pause() pour arreter l'execution du test et faire du step by step. Sinon il existe un autre script livré avec, elementexplorer.js qui permet d'utiliser Protractor de manière intéractive, comme une console, avec autocompletion, ce qui permet de débugguer au fur et à mesure.

Le fichier de config possède aussi une méthode onPrepare qui peut être executé avant de lancer les tests (par exemple pour identifier l'utilisateur sur la page de login). (Tip : element n'est pas dispo dans cette méthode, il faut utiliser browser.driver.findElement). On peut ensuite faire une boucle browser.driver.wait jusqu'à ce que le user soit correctement identifié avant de lancer les tests suivants. Si nécessaire, on peut aussi passer des variables globales aux tests depuis cette méthode avec browser.params.

Les tests e2e prennent du temps, car ils simulent de vraies intéractions utilisateurs, latence comprise. C'est donc un peu plus compliqué d'intégrer ça dans une démarche d'intégration continue. Les prochaines versions de Protractor permettront de sharder les tests sur plusieurs serveurs.

Attention aussi aux applications qui font du long-polling. Comme Protractor attends la fin des requetes pour passer à la suite, il restera bloqué sur un appel comme ça. Il faut alors soit trouver une autre méthode, soit désactiver le long-polling pour les tests. Attention aussi, Protractor (v0.2.11) n'est pas encore compatible avec IE11.

Finalement, un conseil : Les tests e2e sont fragiles, ils sont très dépendents de l'UI et cassent facilement. Une bonne pratique est de rajouter un middleware orienté métier, avec des méthodes comme "selectLoginButton", "fillUserName", etc qui sont les méthodes exposées aux équipes chargées des tests, et si l'UI change il suffit de le modifier à un seul endroit facilement, sans avoir à changer tous les tests. En plus, cela permet aux testeurs de se concentrer sur la feature à tester sans devoir se farcir les détails d'implémentation.


Tags : #ngparis

Want to add something ? Feel free to get in touch on Twitter : @pixelastic