Test fonctionnel des compétences de Yandex Alice sur Node.js

Alice, lance la compétence


Près d'un an s'est écoulé depuis que l'occasion de créer vos compétences pour Alice, l'assistante vocale de Yandex, est apparue. De nouvelles compétences arrivent quotidiennement dans le catalogue , et leur nombre total est de plusieurs centaines. Malheureusement, la communication avec certaines compétences, pour ne pas dire grand-chose, ne correspond pas. La compétence boucle soit sur la même phrase ou est généralement interrompue et ne répond pas.


Dans cet article, je vais envisager d'écrire des tests automatisés fonctionnels pour une compétence sur Node.js. La présence de tels tests vous permet de créer de meilleures compétences et donne confiance en leurs performances.


Outils de test existants


Skill for Alice est un serveur Web qui peut répondre aux demandes POST dans un format spécifique. Pour le moment, il existe plusieurs outils où vous pouvez transférer l'URL de la compétence et vérifier son fonctionnement:


  • La console développeur officielle, où vous pouvez tester vos compétences avec le texte et regarder les demandes / réponses
  • Aimylogic Station Simulator, prend en charge la voix
  • Projet open-source dialogs.popstas.ru pour le test local des compétences

La particularité de ces outils est qu'ils offrent une interface utilisateur pour les tests de compétences manuels. Je veux dans les meilleures traditions d' intégration continue
exécutez la commande dans la console, vérifiez automatiquement tous les scripts, puis téléchargez la nouvelle version uniquement.


Dans le même temps, je ne veux pas me plonger dans les tests unitaires des modules de compétences individuels. Le protocole de requête / réponse est enregistré dans la documentation , et c'est à ce niveau qu'il vaut mieux tester. Ensuite, même après avoir complètement réécrit l'architecture interne, vous n'aurez pas à modifier les tests. Autrement dit, ce sont des tests fonctionnels .


Je n'ai pas trouvé de bibliothèque complète pour Node.js pour une telle tâche, nous allons donc écrire la notre :)



Prenez l' exemple de compétence officiel du référentiel Yandex sur GitHub. C'est la compétence "Parrot", qui répète simplement tout ce que l'utilisateur a dit. Construit sur la base du micro framework et ne contenant que quelques lignes de code:


// server.js const micro = require('micro'); const {json} = micro; module.exports = micro(async req => { const {request, session, version} = await json(req); return { version, session, response: { text: request.original_utterance || 'Hello!', end_session: false, }, }; }); 

Au premier appel, la compétence recevra un message vierge de l'utilisateur (original_utterance) et répondra "Hello!" . Dans d'autres cas, il copie simplement le message de l'utilisateur dans le champ response.text .


J'ai encapsulé l'exemple de code GitHub original dans la fonction micro() pour que l'exportation renvoie un serveur http, que nous utiliserons dans les tests.


Plan de test


Donc, pour couvrir une telle compétence avec des tests, vous avez besoin des éléments suivants:


  1. Élever un serveur avec compétence sur le port local
  2. Vérifiez deux cas:
    • L'utilisateur entre la compétence, la compétence doit répondre "Bonjour!"
    • L'utilisateur envoie un message à la compétence, la compétence doit répondre avec le même message
  3. Arrêter le serveur avec compétence et afficher le rapport

En automatisant ces vérifications, vous pouvez les exécuter avant chaque validation et vous assurer que rien n'est cassé.


Nous allons écrire le code de test selon le plan, en utilisant la syntaxe pour mocha . Supposons que nous ayons déjà une classe d' User capable de faire tout ce dont nous avons besoin:


 // test.js const assert = require('assert'); before(done => { //    server.listen(PORT, done); }); it('should get hello on enter', async () => { //     const user = new User(`http://localhost:${PORT}`); //       const response = await user.enter(); //    assert.equal(response.text, 'Hello!'); }); after(done => { //   server.close(done); }); 

Il reste à écrire la classe User et il sera possible d'exécuter le test.


Utilisateur virtuel


La principale chose qu'un utilisateur de test devrait pouvoir faire est d'envoyer des requêtes POST à ​​l'url de la compétence avec des données au format souhaité. Le format de la demande est décrit dans la documentation . Maintenant, nous n'avons pas besoin de tous les champs, donc je n'ai laissé que le nécessaire pour ne pas gonfler l'exemple de code. Classe d' User avec commentaires:


 // user.js const fetch = require('node-fetch'); module.exports = class User { /** *  * @param {String} webhookUrl */ constructor(webhookUrl) { this._webhookUrl = webhookUrl; } /** *     */ async enter() { const headers = { 'Accept': 'application/json', 'Content-Type': 'application/json' }; //    ,  -   const body = this._buildRequest(''); const response = await fetch(this._webhookUrl, { method: 'post', headers, body: JSON.stringify(body), }); const json = await response.json(); return json.response; } /** *        * @param {String} message */ _buildRequest(message) { return { request: { command: message, original_utterance: message, type: 'SimpleUtterance', }, session: { new: true, user_id: 'user-1', session_id: 'session-1' }, version: '1.0' } } }; 

Lancement


Pour commencer, il reste à importer les classes d'utilisateurs et de serveurs dans le fichier de test, et également définir la valeur du port sur lequel le serveur va monter:


 // test.js ... const server = require('./server'); const User = require('./user'); const PORT = 3456; ... 

Installez toutes les dépendances nécessaires:


 npm install micro node-fetch mocha 

Et lancez le test:


 $ mocha test.js ✓ should get hello on enter 1 passing (34ms) 

Tout va bien, le test est réussi!


Mais avant de continuer, vous devez vous assurer que le test fonctionne vraiment. Pour ce faire, remplacez la compétence de réponse "Bonjour!" à "bonjour!" et relancez-le:


 $ mocha test.js 0 passing (487ms) 1 failing 1) should get hello on enter: AssertionError [ERR_ASSERTION]: '!' == 'Hello!' + expected - actual -! +Hello! 

Le test a montré une erreur - comme il se doit.
Maintenant, à coup sûr, nous considérons que le premier cas est couvert.


Nous apprenons à l'utilisateur à communiquer


Le deuxième cas subsiste lorsque l'utilisateur envoie un message à la compétence et doit recevoir le même message. Pour que l'utilisateur puisse "communiquer", j'ai ajouté la méthode say(message) à la classe User . J'ai également fait un peu de refactoring: j'ai fait envoyer des requêtes http à une méthode distincte et je l'ai utilisé dans enter() et say(message) :


 // user.js const fetch = require('node-fetch'); module.exports = class User { /** *  * @param {String} webhookUrl */ constructor(webhookUrl) { this._webhookUrl = webhookUrl; } /** *     */ async enter() { //    ,  -   const body = this._buildRequest(''); return this._sendRequest(body); } /** *     * @param {String} message */ async say(message) { const body = this._buildRequest(message); return this._sendRequest(body); } /** *  http- * @param {Object} body   */ async _sendRequest(body) { const headers = { 'Accept': 'application/json', 'Content-Type': 'application/json' }; const response = await fetch(this._webhookUrl, { method: 'post', headers, body: JSON.stringify(body), }); const json = await response.json(); return json.response; } // ... }; 

Le code de test pour le deuxième cas ressemble à ceci:


 it('should reply the same message', async () => { //   const user = new User(`http://localhost:${PORT}`); //    await user.enter(); //   const response = await user.say('  ?'); //    assert.equal(response.text, '  ?'); }); 

Nous recommençons et nous voyons que les deux tests ont réussi:


 $ mocha test.js ✓ should get hello on enter ✓ should reply the same message 2 passing (37ms) 

Etapes supplémentaires


De la même manière, vous pouvez ajouter des scripts plus complexes à la compétence, en les couvrant de tests. Cela garantira que les nouveaux changements ne se cassent pas.


L'infrastructure de test créée peut également être améliorée:


  • modifier la classe User afin que les champs restants de la demande puissent être modifiés (par exemple, cochez la case que l'utilisateur n'a pas d'écran)
  • connecter la couverture de code (par exemple nyc )
  • suspendre toutes les vérifications sur les crochets de pré-validation / pré-poussée (par exemple en utilisant husky )

J'ai plusieurs compétences, donc je mets la classe d'utilisateurs de test dans un paquet séparé d' alice-tester , peut-être que quelqu'un viendra à portée de main.


J'ai également publié le code de travail complet de l'exemple de l'article sur GitHub . Vous pouvez cloner le référentiel et expérimenter.


Merci de votre attention!

Source: https://habr.com/ru/post/fr441978/


All Articles