Pruebas funcionales de las habilidades de Yandex Alice en Node.js

Alice, lanza la habilidad


Ha pasado casi un año desde que apareció la oportunidad de crear sus habilidades para Alice, la asistente de voz de Yandex. Nuevas habilidades llegan diariamente en el catálogo , y su número total es de varios cientos. Desafortunadamente, la comunicación con algunas habilidades para decirlo suavemente "no cuadra". La habilidad se repite en la misma frase o generalmente se rompe y no responde.


En este artículo, consideraré escribir pruebas automatizadas funcionales para una habilidad en Node.js. La presencia de tales pruebas le permite crear mejores habilidades y le da confianza en su desempeño.


Herramientas de prueba existentes


Skill for Alice es un servidor web que puede responder a solicitudes POST en un formato específico. Por el momento, hay varias herramientas donde puede transferir la URL de la habilidad y verificar su funcionamiento:



La peculiaridad de estas herramientas es que ofrecen una interfaz de usuario para la prueba de habilidad manual. Quiero en las mejores tradiciones de integración continua
ejecute el comando en la consola, verifique automáticamente todos los scripts y solo luego cargue la nueva versión.


Al mismo tiempo, no quiero profundizar en las pruebas unitarias de los módulos de habilidades individuales. El protocolo de solicitud / respuesta se registra en la documentación , y es en este nivel que es mejor probarlo. Luego, incluso después de reescribir completamente la arquitectura interna, no tendrá que cambiar las pruebas. Es decir, en esencia, estas son pruebas funcionales .


No encontré una biblioteca terminada para Node.js para tal tarea, así que escribiremos la nuestra :)



Tome el ejemplo de habilidad oficial del repositorio Yandex en GitHub. Esta es la habilidad "Parrot", que simplemente repite todo lo que dijo el usuario. Construido sobre la base del micro marco y contiene solo unas pocas líneas de código:


// 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, }, }; }); 

En la primera llamada, la habilidad recibirá un mensaje en blanco del usuario (original_utterance) y responderá "Hello!" . En otros casos, simplemente copia el mensaje del usuario en el campo de response.text .


Envuelto el código de ejemplo original de GitHub en la función micro() para que la exportación devuelva un servidor http, que usaremos en las pruebas.


Plan de prueba


Entonces, para cubrir tal habilidad con pruebas, necesitas lo siguiente:


  1. Levante un servidor con habilidad en el puerto local
  2. Marque dos casos:
    • El usuario ingresa a la habilidad, la habilidad debe responder "¡Hola!"
    • El usuario envía un mensaje a la habilidad, la habilidad debe responder con el mismo mensaje.
  3. Detenga el servidor con habilidad y muestre el informe

Al automatizar estas comprobaciones, puede ejecutarlas antes de cada confirmación y asegurarse de que nada se rompa.


Escribiremos el código de prueba de acuerdo con el plan, usando la sintaxis para mocha . Supongamos que ya tenemos una clase de User que puede hacer todo lo que necesitamos:


 // 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); }); 

Queda por escribir la clase User y será posible ejecutar la prueba.


Usuario virtual


Lo principal que un usuario de prueba debería poder hacer es enviar solicitudes POST a la URL de la habilidad con datos en el formato deseado. El formato de la solicitud se describe en la documentación . Ahora no necesitamos todos los campos, así que dejé solo los necesarios para no inflar el código de ejemplo. Clase de User con comentarios:


 // 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' } } }; 

Lanzamiento


Para comenzar, queda importar las clases de usuario y servidor en el archivo de prueba, y también establecer el valor del puerto en el que se elevará el servidor:


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

Instale todas las dependencias necesarias:


 npm install micro node-fetch mocha 

Y ejecuta la prueba:


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

Todo está bien, ¡la prueba ha pasado!


Pero antes de continuar, debe asegurarse de que la prueba realmente funcione. Para hacer esto, reemplace la habilidad de respuesta "¡Hola!" a "hola!" y ejecutarlo de nuevo:


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

La prueba mostró un error, como debería ser.
Ahora, seguro, consideramos que el primer caso está cubierto.


Enseñamos al usuario a comunicarse


El segundo caso permanece cuando el usuario envía un mensaje a la habilidad y debe recibir el mismo mensaje. Para que el usuario pueda "comunicarse", agregué el método say(message) a la clase User . También realicé una pequeña refactorización: hice el envío de solicitudes http a un método separado y lo utilicé dentro de enter() y 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; } // ... }; 

El código de prueba para el segundo caso se ve así:


 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, '  ?'); }); 

Comenzamos de nuevo y vemos que ambas pruebas han pasado:


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

Pasos adicionales


Del mismo modo, puede agregar secuencias de comandos más complejas a la habilidad, cubriéndolas con pruebas. Esto asegurará que los nuevos cambios no se rompan.


La infraestructura de prueba creada también se puede mejorar:


  • modifique la clase de User para que los campos restantes en la solicitud puedan cambiarse (por ejemplo, marque la casilla que el usuario no tiene una pantalla)
  • conecte la cobertura de código (por ejemplo, Nueva York )
  • cuelgue todos los cheques en ganchos de precompromiso / pre-empuje (por ejemplo, usando husky )

Tengo varias habilidades, así que pongo la clase de usuario de prueba en un paquete separado de alice-tester , tal vez alguien sea útil.


También publiqué el código de trabajo completo del ejemplo del artículo en GitHub . Puede clonar el repositorio y experimentar.


Gracias por su atencion!

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


All Articles