Primero, es muy importante reconocer que siempre estamos haciendo tests manualmente y probamos si el código que estamos realizando esta funcionando como queremos, así que no estaremos realizando algo completamente nuevo sino que simplemente automatizaremos el proceso.
Como un favor para nuestro yo futuro (de nada, futuro yo :D), dejemos pruebas automáticas que se puedan correr para verificar la funcionalidad de nuestro proyecto. Sin pruebas, realizar modificaciones sin producir cambios de comportamiento de la aplicación o agregando bugs es prácticamente imposible, aún si es un equipo pequeño de desarrollo o solamente una persona.
No siempre tendremos en mente todos los requerimientos de la aplicación o los casos extremos, así que para evitar dolores de cabeza en el futuro, siempre es mejor comenzar realizando tests.
Primero definamos algunos conceptos:
- Pruebas unitarias: son pruebas que realizaremos de funcionalidad específica de la aplicación (pruebas de las tareas pequeñas). Son ideales para verificar el comportamiento de las funciones que crearemos.
- Pruebas de integración: son pruebas que engloban varias características de la aplicación (pruebas del conjunto de tareas) para verificar el comportamiento de las partes como un todo.
- TDD: es una técnica para desarrollar que se basa en el concepto de la realización de pruebas antes de desarrollar cualquier funcionalidad. El proceso de desarrollo con TDD es:
- Realizar una prueba (que fallará obviamente porque aún no tenemos código escrito).
- Escribir la funcionalidad necesaria para que la prueba pase exitosamente (no escribir nada más, solamente lo necesario para que la prueba pase, aún si es una respuesta hardcoded).
- Iterar (repetir los anteriores pasos).
Y las herramientas que utilizaremos:
- Mocha: es un framework que nos ayudará a realizar las pruebas de una manera sencilla, es prácticamente plug and play, por lo que no necesita demasiadas configuraciones.
- Chai: que nos permitirá afirmar que realmente el resultado que obtenemos es el que esperamos (assert).
- Istanbul: herramienta que nos generará un reporte de la cobertura de nuestros tests sobre el código fuente.
- Nyc: línea de comandos para Istanbul que nos permitirá correr con ecma6
Primero, descarguemos el proyecto hasta el último post:
$ git clone https://github.com/feardarkness/chat-backend.git
$ git checkout tags/twitter-api
Ahora instalemos todo lo necesario para realizar las pruebas y la cobertura:
$ npm install mocha --save-dev
$ npm install chai --save-dev
$ npm install istanbul --save-dev
$ npm install babel-core --save-dev
$ npm install nyc --save-dev
$ npm install chai --save-dev
$ npm install istanbul --save-dev
$ npm install babel-core --save-dev
$ npm install nyc --save-dev
Además, agregaremos al archivo package.json la instrucción para realizar los tests con mocha (añadimos --compilers para que babel compile nuestros tests escritos con ecma6 :D):
"scripts": { "start": "DEBUG=chat-backend babel-watch -L index.js", "eslint": "eslint .", "test": "mocha tests/**/* --compilers js:babel-core/register" },
También crearemos un archivo (vacío, por el momento) llamado mocha.opts para configurar los tests con mocha en el futuro.
Por último, realizaremos pruebas de nuestro servicio que consume la api de twitter, lo único que nos interesa es que el contenido de los mensajes tenga el tag que requerimos y que la cantidad de mensajes devueltos sea correcta. Existen dos "agrupadores" importantes para los tests, describe e it; describe permite hacer grupos de tests o de funcionalidades (puede anidarse), mientras que it engloba al test que vamos a realizar.
Creemos pues dos tests, uno que verifica que el contenido de los mensajes sea el que pedimos, y otro que verifique la cantidad de mensajes devueltos (Si bien podríamos realizar los tests en uno solo, la idea de cada test es probar una funcionalidad particular, es por eso que realizamos dos tests en este caso).
/* global describe, it*/ import chai from 'chai'; import twitterService from '../services/twitter'; const assert = chai.assert; describe('Api de consumo de twitter', () => { describe('Búsqueda por tag', () => { it('Debe retornar datos con el tag nodejs', (done) => { twitterService.searchByTag('nodejs') .then((data) => { data.statuses.forEach((status) => { assert.include(status.text.toLowerCase(), 'nodejs', 'El texto del mensaje contiene la palabra nodejs'); }); done(); }) .catch((err) => { done(err); }); }); it('Debe retornar la cantidad de datos solicitada', (done) => { twitterService.searchByTag('nodejs', 2) .then((data) => { const totalDeDatosDevueltos = data.search_metadata.count; assert.equal(totalDeDatosDevueltos, 2, 'Debe retornar dos datos'); done(); }) .catch((err) => { done(err); }); }); }); });
Primero, registramos como variables globales describe e it para que el linter no nos muestre errores, luego agrupamos los test con los mensajes Api de consumo de twitter y búsqueda por tag, y dentro creamos los tests; el primero para verificar que la palabra nodejs se encuentre en cada estado devuelto por twitter (assert.include se utiliza para buscar el segundo parámetro en el primer parámetro, funciona con arrays y cadenas) y el segundo para asegurarnos que la cantidad de datos devueltos es la correcta (assert.equal verifica la igualdad de dos valores). Es necesario siempre llamar a la función done() cuando el test termina, y si terminara con error se llama a la misma función pero enviándole el error obtenido done(error).
La estructura de carpetas debería quedar de la siguiente manera:
Carpetas para los tests |
$ npm test
Además, solo para verificar cómo se ve un test con error, haré que la api devuelva un dato más de lo debido:
Error |
Como se puede observar en la imagen, el mensaje de error es claro, debía devolver dos datos pero esta devolviendo tres.
Por último, vamos a mostrar la cobertura de nuestras pruebas sobre el código, esto implica que podemos ver cuánto código está siendo probado realmente (por ejemplo en un if puede que solamente estemos probando el lado verdadero de la condición y no el falso), esto implica ver si nuestros tests están pasando por todas las rutas de código posibles. Modificaremos nuevamente nuestro package.json y le agregaremos la opción de ver la cobertura de los tests sobre nuestro código:
Nota: La línea de test no debe tener saltos de línea (enter), la bajé para que se note y no sobresalga demasiado del template del blog.
Ahora podemos hacer correr los tests nuevamente y veremos un reporte de la cobertura de nuestros tests:
$ npm test
Por último, podemos generar el reporte en html y verificar qué líneas de código no estamos recorriendo con nuestros tests (el reporte se genera dentro de la carpeta coverage).
$ npm run gen-html-coverage-report
Y podemos observar el reporte en html:
Podemos observar en el reporte a detalle que la parte sin tests (las líneas de código que no estamos probando) son el caso de error al llamar a la api de twitter, sin embargo, no siempre es necesario tener un 100% de cobertura de la aplicación, por ahora lo dejaremos con la cobertura actual y agregaremos en caso de ser necesario (se debe tomar en cuenta que el tiempo para realizar una cobertura del 100% suele ser demasiado para los product managers, por lo tanto es bueno quedar en una cobertura realista como de 70% u 80%).
Y eso es todo por ahora, fue más de lo que esperaba para un post pero en definitiva es muy útil para cualquier tipo de proyecto.
Como en todos los demás posts, puedes bajar el proyecto directamente de git con:
$ git clone https://github.com/feardarkness/chat-backend.git
$ git checkout tags/test-y-cobertura
Y eso es todo por ahora, recuerden, sigan programando >_<
Nota: Si necesitan algún detalle extra de nodejs u otros, simplemente escriban un comentario en el post.
Por último, vamos a mostrar la cobertura de nuestras pruebas sobre el código, esto implica que podemos ver cuánto código está siendo probado realmente (por ejemplo en un if puede que solamente estemos probando el lado verdadero de la condición y no el falso), esto implica ver si nuestros tests están pasando por todas las rutas de código posibles. Modificaremos nuevamente nuestro package.json y le agregaremos la opción de ver la cobertura de los tests sobre nuestro código:
"scripts": { "start": "DEBUG=chat-backend babel-watch -L index.js", "eslint": "eslint .", "test": "nyc --require babel-core/register -x tests/**/* node_modules/.bin/_mocha tests/**/* --compilers js:babel-core/register", "gen--html-coverage-report": "nyc report --reporter=html" },
Nota: La línea de test no debe tener saltos de línea (enter), la bajé para que se note y no sobresalga demasiado del template del blog.
Ahora podemos hacer correr los tests nuevamente y veremos un reporte de la cobertura de nuestros tests:
$ npm test
Respuesta de los tests con cobertura de istanbul |
Por último, podemos generar el reporte en html y verificar qué líneas de código no estamos recorriendo con nuestros tests (el reporte se genera dentro de la carpeta coverage).
$ npm run gen-html-coverage-report
Y podemos observar el reporte en html:
Reporte general de istanbul |
Reporte detallado |
Y eso es todo por ahora, fue más de lo que esperaba para un post pero en definitiva es muy útil para cualquier tipo de proyecto.
Como en todos los demás posts, puedes bajar el proyecto directamente de git con:
$ git clone https://github.com/feardarkness/chat-backend.git
$ git checkout tags/test-y-cobertura
Y eso es todo por ahora, recuerden, sigan programando >_<
Nota: Si necesitan algún detalle extra de nodejs u otros, simplemente escriban un comentario en el post.
No hay comentarios:
Publicar un comentario