Para instalar Mocha, en el directorio de nuestro proyecto (y suponiendo que ya tenemos creado un package.json
) hacemos
npm install -g --save-dev mocha
-g
hace que la herramienta CLI mocha
se instale globalmente para poder usarse en otros proyectos. De lo contrario se instalaría dentro de node_modules
. Con --save-dev
añadimos la dependencia al proyecto pero en modo desarrollo.
Si en un proyecto Node bajado de la web hacemos
npm install
para instalar las dependencias, se instalarán todas (también las de desarrollo), pero si hacemosnpm instal --production
las de desarrollo no se instalarán.
Por ejemplo, supongamos que tenemos el siguiente módulo, sobre el que vamos a hacer unas cuantas pruebas:
var obj = {
saludar: function() {
return "hola mundo";
}
};
module.exports = obj;
El fichero con la suite de pruebas tendría un formato como el siguiente:
var s = require('saludador');
var assert = require('assert');
describe('mi suite', function(){
it ('saludar() debería ser una función', function(){
//comprueba que es true, o sea no es undefined
assert(s.saludar);
//comprueba que es una función
assert.equal("function", typeof(s.saludar));
});
it('saludar() debería devolver hola mundo', function(){
assert.equal("hola mundo",s.saludar());
});
})
Como ya hemos visto en muchos ejemplos, gran parte del código Javascript del "mundo real" es asíncrono. Por ejemplo el siguiente código, que hace una petición HTTP para obtener la página principal de la UA:
//para simplificar el código usamos el paquete 'request'
//npm install --save request
request('http://www.ua.es', function(error, respuesta, body) {
console.log(body);
});
Supongamos que queremos comprobar que la página de la UA contiene la cadena "Universidad de Alicante". En una primera instancia, podríamos hacer algo como:
describe('mi suite asíncrona', function(){
it ('la petición a www.ua.es contiene el nombre de la UA', function(){
request('http://www.ua.es', function(error, respuesta, body) {
//indexOf devuelve la posición de una subcadena dentro de otra
//si la subcadena no está devuelve -1
assert(body.indexOf("Universidad de Alicante")!=-1);
});
});
})
Aunque el test anterior va a pasar, en realidad es porque no se está comprobando nada. Mocha ejecuta la suite, y antes de que llegue la respuesta de la UA (y se ejecute el callback con el assert
) la suite ya ha terminado de ejecutarse. El assert no llega nunca a ser comprobado.
Puedes verificar que lo anterior es cierto cambiando la cadena buscada por cualquier otra que no esté en la página. Verás que el test sigue pasando sin problemas
La solución es ejecutar una función que le indique a Mocha que ya han acabado nuestros tests. Hasta que no se ejecute esta función Mocha considerará que el test no ha acabado y por tanto "esperará". Dicha función nos la pasa Mocha en el callback asociado a cada test, y en la documentación y ejemplos se suele llamar done
:
describe('mi suite asíncrona', function(){
it ('la petición a www.ua.es contiene el nombre de la UA', function(done){
request('http://www.ua.es', function(error, respuesta, body) {
//seguro que este assert se ejecuta, todavía no hemos hecho done()
assert(body.indexOf("Universidad de Alicante")>=0);
//ya ha acabado el test
done();
});
});
})
supertest
Para hacer pruebas de APIs REST nos viebe bien poder realizar peticiones de modo sencillo. Para esto podemos ayudarnos del paquete supertest
. Lo instalaremos, como viene siendo habitual con:
npm install --save-dev supertest
Por ejemplo supongamos que tenemos el siguiente código Express que queremos probar:
var express = require('express');
var app = express();
//En Express asociamos un método HTTP y una URL con un callback a ejecutar
app.get('/', function(pet,resp) {
//Tenemos una serie de primitivas para devolver la respuesta HTTP
resp.status(200);
resp.set('X-Mi-Cabecera', 'Hola')
resp.send('Hola soy Express');
});
//Este método delega en el server.listen "nativo" de Node
app.listen(3000, function () {
console.log("El servidor express está en el puerto 3000");
});
module.exports = app;
La mejor forma de ver el uso de supertest
es a través de un ejemplo:
var hola_express = require('hola_express');
var supertest = require('supertest');
describe('prueba de la app web', function(){
it('/ devuelve el contenido adecuado', function(done){
//Al objeto supertest le pasamos la app de Express
supertest(hola_express)
//Hacemos una petición HTTP
.get('/')
//Supertest incluye sus propias aserciones con 'expect'
//Cuando ponemos un entero estamos verificando el status HTTP
.expect(200)
//Cuando ponemos dos String estamos verificando una cabecera HTTP
.expect('X-Mi-Cabecera', 'hola')
//Si ponemos un string estamos verificando el cuerpo de la respuesta
//Como esta ya es la última expectativa, pasamos el 'done'. Supertest lo llamará
//Cualquier 'expect' admite el 'done' como último parámetro
.expect('Hola soy Express', done);
});
it('La ruta /hola no existe', function(done){
supertest(hola_express)
.get('/hola')
.expect(404, done);
});
});
Supertest
usa internamente la librería superagent
, así que podemos consultar en la documentación de esta última cómo realizar otro tipo de peticiones como POST
o PUT
Si las formas de expect
que ya hemos visto no se adaptan a nuestras necesidades, siempre podemos pasar una función en la que pondremos los assert
que queramos. Dicha función recibirá automáticamente como parámetro un objeto response
de superagent
.
supertest(hola_express)
.get('/')
.expect(function(respuesta){
//En la referencia del objeto response de superagent
//podemos ver la propiedad 'text': el texto "raw" del cuerpo de la respuesta
assert(respuesta.text.indexOf('Express')!=-1);
})
//El callback de antes no recibe el 'done', así que tenemos que usar 'end',
//que acaba el test llamando a la función que le pasemos
.end(done);