Utiliser un Framework End-To-End (E2E) pour générer des vidéos avec synthèse vocale

Introduction E2E

L’architecture micro-services que nous utilisons pour construire notre offre SaaS ouvre beaucoup de possibilités. Elle permet d’utiliser différentes stacks techniques et de pousser du code en production rapidement.

Ce mode de fonctionnement soulève plusieurs problématiques que nous avons dû prendre en compte :
  1. Plusieurs services sont impliqués dans chaque fonctionnalité ce qui entraîne plus de complexité.
  2. Les livraisons en continu nous obligent à automatiser les campagnes de tests de manière à ne pas avoir de longues phases de recette manuelles ou de non régression.
  3. Afin de garantir la fiabilité des interfaces pour nos utilisateurs, les tests unitaires et d’intégrations techniques sont des outils indispensables mais insuffisants.
Nous avons donc créé un Framework de tests End-To-End (E2E) : un niveau de tests supplémentaire qui utilise directement les interfaces graphiques en simulant le comportement des utilisateurs, mais avec plusieurs innovations, comme celle consistant à générer des vidéos avec explications en synthèse vocale.


Rappel sur les 3 niveaux de tests


Les Tests unitaires s’appliquent sur de petites portions du code. Ils sont les plus nombreux, les plus rapides et sont lancés systématiquement à chaque changement. Une bonne pratique étant de les garder le plus rapides possible de manière à ne pas ralentir le travail des développeurs. Un problème détecté par ces tests est simple à analyser la plupart du temps.
Les Tests d’intégration testent plusieurs briques du système. Ils sont moins nombreux, plus lents et sont lancés moins souvent. Les problèmes détectés deviennent plus complexes et peuvent nécessiter une analyse de plusieurs services. C’est ici que sont vérifiés les formats de contrats d’échange entre service.
Les Tests E2E sont les plus longs à exécuter. Ils garantissent toute la chaîne puisque ils interrogent le navigateur de la même manière qu’un utilisateur. Une fois qu’ils sont validés, nous sommes sûr que l’application fonctionne correctement. Cette confiance nous permet de livrer souvent en production sans dégrader le service. A noter que des tests E2E peuvent ne pas être graphiques (Ex: Services pour le Machine To Machine), mais ici nous ferons un focus sur les tests E2E de type navigateur.

Comment cela fonctionne ?


Au cœur de notre système E2E nous utilisons Selenium, un Framework sous Licence Apache qui permet de piloter les navigateurs web. Le principe est de lancer une instance de navigateur web comme Chrome ou Firefox, puis de se connecter aux interfaces graphiques. Nous avons encapsulé Selenium de sorte d'appliquer le pattern Page / Scenario / Test, qui consiste à :

  • Page : encapsulation de tous les éléments de la page via des sélecteurs pour pouvoir retrouver des éléments du DOM par id, css, xpath. Nous privilégions au maximum l'utilisation des id, nettement moins sujets à des régressions en cas de changement de layout de la page.
  • Scenario : un parcours simple utilisateur avec des données saisies
  • Test : une combinaison d'un ou plusieurs scénarios


Selenium permet ensuite de définir simplement des scénarios de tests composés d’actions simples :

  • Remplir un champ
  • Sélectionner une valeur dans un menu
  • Appuyer sur un bouton
  • etc.

Un scénario peut être vu comme une succession d’actions. Les actions sont exécutées dans le navigateur et des tests de cohérence sont réalisés au fur et à mesure comme dans un test unitaire.

listTaskPage.getNewName().fill(taskName);
listTaskPage.getNewDescription().fill(taskDescription);
listTaskPage.getCreateButton().click();

Nous remplissons les champs de saisie, puis cliquons sur le bouton

assertEquals(startCount + 1, startCountAfter);
listTaskPage.getTaskName(0).hasContent(taskName);

Vérifications de valeurs

Chaque scénario génère un rapport de tests de manière à pouvoir analyser le comportement de l'application. En cas d’erreur, le rapport indique les étapes qui ne sont pas passées et cela donne un point de départ pour l’analyse. Ainsi nous détectons les éventuels problèmes dans les interfaces et pouvons intervenir très rapidement de manière pro-active.

Ces tests sont lancés sur des environnements identiques à la production et auxquels les utilisateurs n'ont pas accès. L'impact des éventuels problèmes détectés est donc limité. Nous pouvons passer en production, lorsque le niveau de confiance sur le système est élevé.

Le Framework permet l’exécution en mode « headless » (supporté par Chrome et Firefox depuis 1 an environ) dans lequel le navigateur s’exécute en tâche de fond sans affichage. Nous pouvons ainsi faire tourner nos campagnes de tests sur des serveurs Linux sans interface graphique.

Contrainte : les tests E2E sont longs à s’exécuter


Les campagnes de tests E2E ont un périmètre de couverture très important, mais leur temps d’exécution l’est également. Il est assez fréquent que l’exécution de l’ensemble prenne plusieurs jours.

En effet ils utilisent des interfaces graphiques dont les mécanismes sous-jacents sont complexes et impliquent plusieurs systèmes : des bases de données, des appels réseau, des files de messages, etc. De plus les interfaces peuvent avoir plusieurs cas d’utilisations et différents contextes, ce qui multiplie les cas de tests.

Ainsi des tests couvrant l’ensemble du système nécessitent un temps d’exécution très supérieur à celui d’un test unitaire sur une bibliothèque, lequel peut s’exécuter en mémoire.

Ces tests nécessitent également une infrastructure complète.

Management grâce aux suites JUnit

Ces contraintes de temps d’exécution ne s’intègrent pas facilement dans une stratégie de développement en cycles courts et livraisons fréquentes, car cela risque de ralentir le développement.

Pour répondre à cette problématique nous définissons des périmètres de tests différents en utilisant les principes de suites de Junit. Une suite rassemble plusieurs scénarios qui s’exécutent séquentiellement. Les suites ayant des cycles de vie différents agrègent les scénarios et couvrent des périmètres de tests plus ou moins grands.

Un exemple d’organisation possible :

  • Tests de base : couvrent les éléments de base et s’exécutent en 1 minute. Cette suite est exécutée par Jenkins, notre outil de Code Integration (CI), à chaque fois qu’un développeur pousse du code. Cette suite garantit qu’il n’y a pas de régression majeure, sans ralentir le développement.
  • Tests de couverture maximum : Cette suite couvre l’ensemble du périmètre fonctionnel. Elle contient plusieurs dizaines de scénarios et prend 1 heure à s’exécuter. Elle peut être lancée une fois par semaine et/ou avant une mise en production.
  • Tests avec vidéo : un autre cas d’utilisation du Framework.


@SuiteClasses({ TaskManagementScenario.class, TaskManagementAdvancedScenario.class })
@E2ESuite(name = "ci-test-suite")
public class TestSuite extends Suite {

}
Le paramétrage du périmètre d'une suite en listant les scénarios

Calcul du taux de couverture des tests

Afin d’être sûr que nos suites de tests couvrent une grande partie du périmètre fonctionnel, nous calculons un indicateur de couverture = coverage pour chaque micro service. Il nous permet de savoir de manière précise si le service est suffisamment « couvert » par les tests ou pas.

Pour cela nous utilisons différentes technologies selon les environnements de développements utilisés :
  • Jacoco pour les développements Java
  • Istanbul pour des développements Javascript - les interfaces graphiques notamment
Le principe est d'utiliser des agents pour exécuter le code de manière instrumentalisée pendant les tests, afin de détecter quelles portions de codes sont exécutée. Un rapport indique ensuite de manière très précise les lignes exécutées et non exécutées. Nous sommes ainsi capables d’identifier les zones non couvertes et de calculer le taux de couverture.


En bleu le service JAVA API est instrumentalisé via Jacoco
Au lancement de la suite, des tests Cucumber lance des requêtes REST 
=> à la fin Jacoco fournit le rapport
Les services en vert font partie de l'architecture mais ne sont pas instrumentalisés



En bleu, le framework E2E lance une suite de tests. Le client riche en ReactJS est exécuté dans un navigateur Web et l'agent Istanbul surveille l’exécution, puis génère un rapport
En vert, nous utilisons toujours le même backend - non instrumentalisé ici


Afin de garantir un taux de couverture élevé pour tous nos services, nous avons rendu cet indicateur bloquant dans notre outil de qualité de code : SonarQube. Les taux peuvent être définis en fonction du type de projet. Le taux minimum est plus élevé pour une bibliothèque partagée par exemple.

Génération de vidéos – une des prochaines étapes


Notre Framework permet de construire des scénarios qui s’exécutent dans le navigateur web. Une fois les scénarios définis, nous pouvons alors lancer les tests et les voir s’exécuter sur notre écran de manière automatique.

Nous avons souhaité utiliser ce mode de fonctionnement pour créer des vidéos commentées. Cela nous permettra à terme de donner de la visibilité sur nos produits de manière automatisée.

Pour cela nous capturons l’écran sur la zone du navigateur, lorsque le Framework déroule le scénario : nous obtenons une piste vidéo.

La bibliothèque Java utilisée est monte-screen-recorder

// setup output format and video format
fileFormat = new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI);
Format screenFormat = new Format(
MediaTypeKey,
MediaType.VIDEO,
EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
CompressorNameKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
DepthKey, 24,
FrameRateKey, Rational.valueOf(30),
QualityKey, 1.0f,
KeyFrameIntervalKey, 15 * 60);

// define the capture zone = the web browser
Rectangle driverCaptureSize = new Rectangle(
driverWindow.getPosition().x, driverWindow.getPosition().y,
driverWindow.getSize().width, driverWindow.getSize().height);

// graphical environment
GraphicsConfiguration gc = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration();

this.screenRecorder = new SpecializedScreenRecorder(
gc, driverCaptureSize, fileFormat, screenFormat, null, null,
targetFolder, filePrefixName);

this.screenRecorder.start();

// Nous lançons l'enregistrement et laissons le scénario se dérouler
this.screenRecorder.stop();


Le fichier vidéo est enregistré à l'arrêt de l'enregistrement.

Text-To-Speech 

Pour l’audio, nous définissons un script de commentaires que nous ajoutons au scénario. Ces commentaires sont alors affichés sous forme de fenêtres popup dans la vidéo - comme s’il s’agissait de sous-titres. Cela permet d’expliquer l’utilisation de l’interface graphique et peut être ainsi utilisé comme outil de démonstration et de formation.

Pour un meilleur ressenti, nous utilisons le projet Amazon Polly pour convertir le texte en enregistrement audio et l'incrustons dans la vidéo. AWS héberge une partie de nos applications et le service Polly nous permet d’obtenir l’audio dans la langue souhaitée en utilisant des appels API, via le SDK Amazon. Nous avons testé le service avec des textes anglais et français. Pour une langue donnée, il est possible de choisir parmi plusieurs voix. Pour le moment les résultats sont prometteurs et nous attendons des améliorations de ce service.

SynthesizeSpeechRequest synthesizeSpeechRequest = new SynthesizeSpeechRequest();
synthesizeSpeechRequest.setText(text);
synthesizeSpeechRequest.setVoiceId(requestVoiceId);
synthesizeSpeechRequest.setLanguageCode(languageCode);
synthesizeSpeechRequest.setOutputFormat(OutputFormat.Mp3);
SynthesizeSpeechResult synthesizeSpeechResult = amazonPollyClient.synthesizeSpeech(synthesizeSpeechRequest);
return synthesizeSpeechResult.getAudioStream();

Un appel à AWS Polly => nous obtenons un MP3 à partir d'un texte

Nous utilisons l’exécutable ffmpeg pour manipuler les fichiers vidéo et audio

ffmpeg.exe -i video.avi -i audio.mp3 -c:v copy video_audio_1543917136779.avi


Fusion de pistes video et audio pour obtenir une vidéo avec le son

L’un des grands avantages de cette approche est que nous obtenons la vidéo sans avoir à faire un important travail de réalisation et de montage. De plus, lorsque nous ajoutons des évolutions sur l’application, nous avons simplement à changer le scénario dans la section en question pour obtenir une nouvelle vidéo enrichie et mise à jour.




Un commentaire est visible au premier plan de la vidéo et le texte est "lu" par la synthèse vocale

Conclusion

Selenium, bien que concurrencé par un certain nombre d'outils (tels que cypress.io qui propose lui aussi la vidéo) reste un candidat de choix pour faire des tests d'intégration d'applications. Avec un minimum d'encapsulation de Selenium afin d'éviter le couplage, il nous a permis quasiment sans frais (les scénarios de tests n'ont pas dû être modifiés sauf pour ajouter le texte des commentaires) d'ajouter la création de vidéos avec des commentaires et la lecture en synthèse vocale de ces commentaires. Tous ces éléments nous permettent avec succès dès aujourd'hui de livrer aussi souvent qu'on veut (à minima 1 fois par semaine). Nous avons encore bien d'autres idées permettant de maximiser les synergies de tous les efforts que nous faisons dans le projet à automatiser, améliorer notre couverture de code. Dans l'exemple de la vidéo, on garantit ainsi à à des acteurs internes (avant-vente, formation) d'avoir en permanence un matériel documentaire sous forme de vidéos le plus à jour possibles avec les dernières versions de notre logiciel SaaS.

Auteurs

Mathias Carville : Architecte Technique à Cegedim Insurance Solutions


Aucun commentaire:

Enregistrer un commentaire