Testeando Javascript (1)

Introducción.

En esta serie de artículos nos proponemos tratar acerca del testing de aplicaciones desarrolladas con Javascript.

La elección de los temas viene marcada por situaciones prácticas que se nos han presentado en nuestro trabajo de desarrolladores web. Es decir, no pretendemos abarcar todos los aspectos de esta temática, ni tampoco abordaremos aspectos demasiado teóricos. Cuando creamos conveniente se harán referencias a recursos disponibles, en español u otros idiomas, que permitan profundizar aquellos apartados que nosotros tratemos superficialmente.

Considera estos artículos fundamentalmente como una iniciación o punto de partida. Sería importante, si quieres sacar el máximo provecho, que realices las prácticas aquí expuestas. Por supuesto, estamos abiertos a comentar lo que creas conveniente. Sin más preámbulos, manos a la obra. Nuestro primer artículo tratará de Express.

Testeando una aplicación Express.

Probaremos una sencilla aplicación Express que corre en un servidor Node. Comprobaremos que las respuestas proporcionadas se corresponden con valores esperados y deseados.

El plan.

Crearemos un proyecto desde cero. Las tareas que llevaremos a cabo se pueden dividir en los siguientes apartados:
1. Poner en marcha nuestra aplicación:
* crear la estructura de carpetas y archivos del proyecto.
* instalar express.
* nuestra aplicación será codificada en ES6 y por lo tanto necesitaremos babel.
* una buena práctica es codificar de acuerdo a standards seguidos por la mayoría de los programadores avanzados y por lo tanto necesitaremos eslint.
2. Utilizar las herramientas de test:
* instalar Mocha
* instalar Chai
* instalar request
* codificar nuestros tests.

Poner en marcha nuestra aplicación.

Se espera que tengas instalado en tu plataforma Node y npm a ser posible que estén actualizados. Yo utilizo Linux Mint pero supongo que los comandos valdrán igualmente en tu OS. En tu carpeta de proyectos crea una carpeta, p.ej., testing y sitúate en ella para ejecutar los comandos siguientes:

$ mkdir src test dist
$ touch src/server.js test/server.js
$ touch .babelrc .eslintrc
$ tree -a
.
├── .babelrc
├── dist
├── .eslintrc
├── src
│   └── server.js
└── test
└── server.js

3 directories, 4 files

El código puede variar en tu OS. A partir de ahora, los comandos funcionarán cualquiera que sea éste.

Inciamos npm:

$ npm init -y
Wrote to /home/antonio/Proyectos/pruebas/testing/package.json:

{
"name": "testing",
"version": "1.0.0",
"description": "",
"main": "index.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

Completa o cambia los datos si así lo deseas. Empezaremos a instalar los packages que necesitaremos. Primero, las devDependencies

$ npm i babel babel-cli babel-preset-es2015 eslint babel-eslint --save-dev

Escribiremos esto en .babelrc:

{
"presets": ["es2015"]
}

y esto en .eslintrc:

{
"parser": "babel-eslint",
"env": {
"node": true,
"mocha": true
},
"rules": {
"object-curly-spacing": 1,
"no-undef": 1
}
}

A continuación instalamos express:

$ npm install express --save

Finalizadas, de momento, las instalaciones empezaremos con el código propiamente dicho. Editamos el archivo src/server.js

/**
* src/server.js
*/

const express = require("express");
const app = express();

app.get("/", (req, res) => {
const text = 'welcome to my homesite';

res.send(text);
});

app.listen(3000);

Una aplicación muy sencilla pero válida para nuestros propósitos. Añadiremos la siguiente línea a la sección de script del package.json:

"start": "./node_modules/.bin/babel-node ./src/server.js",

y ejecutamos:

$ npm start

> testing@1.0.0 start /home/antonio/Proyectos/pruebas/testing
> babel-node ./src/server.js

que pondrá en marcha nuestro servidor. Si abrimos un navegador por http://localhost:3000 veremos el sencillo mensaje de bienvenida. Es importante tener en cuenta que la ejecución del comando continúa mientras no lo ‘matemos’. En linux haremos CTRL+c para ese propósito.

Hasta ahora, poco nuevo para muchos de nosotros. Pero nos hemos acercado mucho a nuestro objetivo real. De hecho, ya podemos empezar a testear dado que ya tenemos algo que testear.

Utilizar las herramientas de test.

Volvemos al estado instalador. Ejecutemos:

$ npm install mocha chai --save-dev
... ... ...
$ npm install request --save

Las dos primeras son propiamente nuestras herramientas de test y la tercera nos permitirá realizar solicitudes a nuestro servidor y capturar su respuesta.

“* Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser*”. Esta es la manera en la que el equipo de desarrolladores de Mocha lo describen. De este framework utilizaremos tres herramientas:
1. su binario nos permitirá lanzar la ejecución de los test que tengamos preparados.
2. la función describe
3. la función it

Escribamos la siguiente línea en sustitución de la línea “test” en el apartado de scripts de nuestro package.json:

"test": "./node_modules/.bin/mocha –reporter spec || true"

y ejecutemos npm test obtendremos la siguiente salida:

$ npm test

> testing@1.0.0 test /home/antonio/Proyectos/pruebas/testing
> mocha --reporter spec || true

0 passing (3ms)

Es decir Mocha ejecutará nuestros tests. En este caso, aun no hay test que realizar. Una cosa a destacar es que no hace falta indicarle en qué carpeta va a encontrar los test a realizar. De hecho, ejecutará todos los tests que encuentre en la carpeta test.
Bien escribamos nuestro primer test. Éste hará una solicitud a nuestro servidor y comprobará que la respuesta es el mensaje de bienvenida que ya conocimos anteriormente. Para ello editemos el archivo test/server.js

const expect = require("chai").expect
const request = require('request')

describe("Testeando un Node server =>", () => {

describe("Está en funcionamiento? =>", () => {

let url = "http://localhost:3000";

it("Si está funcionando el status de la respuesta deberia ser 200", (done) => {
request(url, (error, response, body) => {
expect(response.statusCode).to.equal(200)
done()
})
})
})
})

Si ejecutamos ahora npm test nos dará esta salida:

$ npm test

> testing@1.0.0 test /home/antonio/Proyectos/pruebas/testing
> mocha --reporter spec || true

Testeando Node server =>
Está en funcionamiento? =>
1) Si está funcionando el status de la respuesta deberia ser 200

0 passing (37ms)
1 failing

1) Testeando Node server => Está en funcionamiento? => Si está funcionando el status de la respuesta deberia ser 200:
Uncaught TypeError: Cannot read property 'statusCode' of undefined
at Request.request [as _callback] (test/server.js:12:24)
at self.callback (node_modules/request/request.js:187:22)
at Request.onRequestError (node_modules/request/request.js:813:8)
at Socket.socketErrorListener (_http_client.js:308:9)
at emitErrorNT (net.js:1272:8)
at _combinedTickCallback (internal/process/next_tick.js:74:11)
at process._tickCallback (internal/process/next_tick.js:98:9)

El test ha fallado porque no se puede leer la propiedad status code de la respuesta. Efectivamente, no habíamos arrancado el servidor. Abramos una nueva consola y ejecutemos npm start y npm testsucesivamente. La salida ahora sería:

$ npm test

> testing@1.0.0 test /home/antonio/Proyectos/pruebas/testing
> mocha --reporter spec || true

Testeando Node server =>
Está en funcionamiento? =>
✓ Si está funcionando el status de la respuesta deberia ser 200 (56ms)

1 passing (67ms)

Enhorabuena, has ejecutado tu primer test OK. Vale hay respuesta, pero ¿es la esperada? Añadimos lo siguiente a continuación de la función it anterior en test/server.js

it("Debería haber un determinado mensaje de bienvenida", (done) => {
request(url, (error, response, body) => {
expect(body).to.equal("welcome to my homesite")
done()
})
})

y ejecutemos de nuevo el test. Esta es la salida ahora:

$ npm test

> testing@1.0.0 test /home/antonio/Proyectos/pruebas/testing
> mocha --reporter spec || true

Testeando Node server =>
Está en funcionamiento? =>
✓ Si está funcionando el status de la respuesta deberia ser 200 (82ms)
✓ Debería haber un determinado mensaje de bienvenida

2 passing (103ms)

Vaya dos test OK de dos posibles. La cosa marcha. Evidentemente, si el body de la response fuera otro (‘welcome, guest’, p.ej.) el segundo test fallaría. También fallaría si hubieramos escrito:

expect(body).to.equal("welcome, guest");

en el test. Bien, ¿no?
Nuestro servidor es muy simple. De hecho solo puede mostrar una página. Si cambiamos la url de nuestro test a http://localhost:3000/books ,p.ej., los dos test fallan porque la respuesta tiene un tatusCode de 404, y el body de la respuesta sería Cannot GET /books. Os animamos a que intentéis modificar src/test.js para comprobar esta afirmación.

Como se puede observar, por cada página (más bien, url) de nuestra web podríamos tener su correspondiente test que comprobara que funciona de acuerdo a lo esperado. Cosa que haremos en un nuevo artículo porque éste se nos ha ido un poco de las manos. Ese nuevo artículo no tendrá tanta tarea previa e irá directamente al grano. O al menos eso esperamos.

Esperamos que os halla sido de utilidad. Cualquier comentario es bienvenido. Gracias por la atención prestada. Hasta la próxima.

Integrando FirePHP con Flight micro php framework

¿Qué es y para que sirve FirePHP?

FirePHP es una extensión del famoso Firebug de Firefox que te ayuda a debugear tus scripts de php. Esto lo hace de tres maneras principalmente:

  1. Permitiendo mandar mensajes personalizados, al modo de print o var_dump(), a la consola de Firebug como podemos ver en esta imagen:
    firephp con flight michocro framework
    El objeto Flight::request() en la consola de Firebug
  2. Mostrando en esa misma consola una detallada información de un error producido en tu script, por ejemplo, al lanzarse una excepción como en la imagen:
    Salida de FirePHP en Firebug
    Pantalla de Firefox mostrando la salida de FirePHP en Firebug
  3. Especialmente útil para probar solicitudes ajax a un script de php.
    Probando ajax con FIrePHP
    Probando ajax con FIrePHP

Instalando FirePHP

Para poder usar esta herramienta debe hacer los siguientes pasos:

  1. Asegúrate de tener instalado el complemento Firebug de Firefox (Herramientas>>Complementos>>Extensiones). De no ser así, instálelo desde esta página de Firefox Addons.
  2. Una vez instalado Firebug, y también desde Firefox Addons, instale FirePHP desde esta página. Deberá verlo en Herramientas>>Complementos>>Extensiones
  3. Descargar la parte del servidor, es decir, los archivos que utilizaremos en nuestros proyectos php que nos permitirán utilizar esta herramienta. Se pueden descargar desde esta página.Una vez descargado y descomprimido, quédese con el archivo FirePHP.class.php, que encontrará en la subcarpeta FirePHPCore-0.3.2/lib/FirePHPCore/, y cópielo a una carpeta de la raíz de su proyecto. En mi caso, lo copiaré a la carpeta vendors. Como utilizo composer añado la siguiente línea a mi composer.json:

    ... ... ...
    "autoload": {
    ... ... ...
    "files":["vendor/FirePHP.class.php"]
    ... ... ...
    }
    ... ... ...

    Una vez realizado composer update ya estará disponible para su uso en el projecto.

Integrando FirePHP en Flight micro framework

Flight micro framework se caracteriza por la facilidad en integrar scripts php de terceros dentro del framework. En el caso que nos ocupa tendremos que utilizar las siguientes líneas de código:

 Flight::register('firephp','FirePHP',array(),function($firephp){
    $firephp->registerErrorHandler($throwErrorExceptions=true);
    $firephp->registerExceptionHandler();
    $firephp->registerAssertionHandler(
        $convertAssertionErrorsToExceptions=true,
        $throwAssertionExceptions=false);
    $options = array('includeLineNumbers' => true);
    $firephp->setOptions($options);
});

A partir de ese momento podremos acceder a los métodos y propiedades definidos en FirePHP.class.php a través de Flight::firephp().
Para los ejemplos que se muestran en las imágenes he utilizado los siguientes:

  • Flight::firephp()->trace(Flight::request()); [Imagen 1]
  • $request = Flight::request();
    Flight::firephp()->log($request->data);
    [Imagen 3]

Si además del código anterior añadimos

Flight::map('error', function(Exception $ex){
    Flight::firephp()->trace('Error detectado');
});

conseguiremos que las excepciones lanzadas por el framework también se muestren en la consola de Firebug [como en la imagen 2].
Bueno, creo que ha llegado el momento de terminar este tema. No estaría de más recordar que en FirePHP encontrarás información complementaria. Como siempre estoy abierto a preguntas, comentarios o críticas. Un saludo.