TDD en Javascript: II parte

13 Feb 2011

En la primera entrega de esta introducción al TDD, hablamos sobre las ventajas y posibles inconvenientes de utilizar la metodología de Desarrollo Dirigido por Tests prestando una atención especial a su uso en Javascript. Si hasta el momento la teoría nos ha resultado lo suficientemente interesante como para comenzar a practicarla, en esta segunda veremos algunas de las herramientas que nos facilitarán su implementación en nuestros proyectos.

En este apartado, la herramienta sin duda más importante será un framework que nos permita aplicar nuestras afirmaciones (asserts) sobre el código que queremos evaluar. Es por eso que durante este artículo, revisaremos algunos de los entornos más importantes.

xUnit Test Frameworks

La familia compuesta por los frameworks de tipo xUnit, continúan siendo los más utilizados para la escritura de tests unitarios pese a que, en los últimos años, están cobrando cada vez una mayor relevancia otros métodos de Desarrollo Dirigido como aquellos basados en el comportamiento (BDD), modelos (MDD) o dominios (DDD). Volveremos más adelante sobre algunas de estas metodologías para ofrecer una imagen de conjunto más completa.

Las características básicas de los frameworks de tipo xUnit son:

  • Entorno especializado. Dado un conjunto de métodos de prueba, el framework proporciona una serie de test que se ejecutan devolviendo los resultados de una forma clara.
  • SetUp o configuración inicial. Para facilitar la creación de tests compartidos, se pueden establecer una serie de reglas o métodos que se ejecutan de forma previa al resto.
  • Funciones predefinidas. En la entrega anterior comentábamos cómo una afirmación básica evalúa si el resultado de una función o método es verdadero o falso. Un framework, sin embargo, proporciona un conjunto de funciones prediseñadas que permiten evaluaciones más expresivas de acuerdo a las necesidades reales que pueden darse durante el flujo de una aplicación.

Cómo se evalúa un entorno

A la hora de seleccionar un framework, debemos prestar atención a tres aspectos: el entorno de ejecución, la calidad de sus afirmaciones y las dependencias.

Entorno de ejecución

En Javascript, por lo general, el entorno de ejecución es el propio navegador web. Esto signifiva que para su funcionamiento, primero debe cargarse un archivo HTML que, a su vez, carga las librerías JS necesarias (las correspondientes al código a evaluar y aquellas necesarias ppata el propio framework). Finalmente, el resultado del test suele pintarse sobre una estructura de etiquetas HTML decoradas con una hoja de estilos (CSS) propia.

Otro tipo de frameworks, sin embargo, utilizan otros entornos como la línea de comandos implementadas con Java o Rhino. El tipo de entorno a elegir dependerá de si trabajamos sobre una aplicación en cliente (el más frecuente), en servidor (por ejemplo node.js) o incluso un plugin o extensión para un navegador. No hay tampoco que olvidar que recientemente, Javascript se está abriendo un hueco importante también como lenguaje de escritorio como queda patente por ejemplo, en su uso masivo en el proyecto GNOME Shell.

Afirmaciones

La calidad de los tests unitarios depende, principalmente, del conjunto de afirmaciones que nos permita utilizar nuestro framework. Cuanto mayor sea la flexibilidad y expresividad de las afirmaciones, los tests serán más claros y precisos. En la mayoría de ocasiones, puede resultar suficiente comparar el resultado obtenido en una función con un valor predefinido. Sin embargo, con afirmaciones más específicas, pueden conseguirse tests más fáciles de leer, de mantener y de modificar. A modo de ejemplo, algunas de las afirmaciones predefinidas más comunes que encontraremos para Javascript son:

  • X es igual a Y. X corresponde al valor que devolverá la función e Y es el valor esperado.
  • X es un valor falso (falsy). Comprueba que el valor obtenido no corresponda con un falsy value.
  • X coincide con patrón Y. Comprueba que la cadena X se corresponde con una expresión regular definida.

Dependencias

Por lo general, un framework debe tener las menores dependencias posibles para garantizar su funcionamiento en el mayor número de entornos. La gran difusión de librerías Javascript como jQuery, Prototype o Mootools ha dado lugar a diferentes frameworks programados en estos sublenguajes que necesitan de los mismos para ejecutarse. Esto resulta un problema para aquellos dispositivos que, como por ejemplo la PlayStation III, carecen de soporte para estas librerías.

Catálogo de Frameworks xUnit

En el momento de escribir este post, los principales frameworks pertenecientes a la familia xUnit son:

Veamos algunos de ellos en detalle:

QUnit

QUnit es a día de hoy uno de los frameworks más populares gracias a su arquitectura. Es un proyecto del jQuery Team elaborado para comprobar la integridad de la propia librería pero cuya flexibilidad la hace aplicable a cualquier código Javascript, ya sea bajo un entorno en lado cliente o incluso en lado servidor. Con una sintaxis extremadamente sencilla, facilita testear incluso código asíncrono.

Ejemplo Sintaxis
test("a basic test example", function() {
  ok( true, "this test is fine" );
  var value = "hello";
  equals( "hello", value, "We expect value to be hello" );
});
Ventajas
  • El paquete incluye todo lo necesario para comenzar su utilización inmediatamente: librería, CSS y HTML inicial.
  • No precisa de ninguna tecnología externa como Java o Rhino sino que todo tiene lugar en el entorno del navegador.
  • Sintaxis muy sencilla y familiar para desarrolladores jQuery.
  • Permite tests sobre código asíncrono.
Desventajas
  • Requiere de jQuery para su ejecución.
  • Requiere de una estructura HTML para pintar los resultados que debemos incluir en nuestro proyecto.
  • La sintaxis, aunque sencilla, no es estándar dentro del mundo de los tests unitarios. En lugar de términos como ‘AssertTrue‘, se utilizan otros propios como ‘ok‘.
  • Sólo tenemos tres tipos de afirmaciones (asserts), lo que puede resultar muy limitado en comparación a otras soluciones.
  • Los tests se asocian al contexto general (global namespace).
Conclusión

Una solución liviana y con muchos seguidores entre la comunidad de desarrolladores; avalada por el jQuery Team, quizá resulta demasiado simple para aplicacines de gran arquitectura. Sin embargo, para proyectos pequeños, puede ser la solución perfecta.

jqUnit

El fin de este proyecto es complementar QUnit aplicándole una capa por encima. No añade funcionalidades nuevas, pero si que permite utilizar una sintaxis estándar además de que mantine limpio el contexto general.

Ejemplo Sintaxis
jqUnit.TestCase.prototype.yep = function( val ){ this.ok( val ); };
var t = new jqUnit.TestCase( 'TestCase' ,function() {
  /*setup*/
  this.yep(1);
},function(){
  /*teardown*/
  this.ok(1)
} );
//jqUnit is mixed into TestCase, so you can overwrite them & only need 1 with(){}
t.test( 'part 1' ,function(){ this.ok( 1 ) } );
t.test( 'part 2' ,function(){ with( this ) { ok( 2 ); yep( 3 ) } } );
t.test( 'part 3' ,function(){ with( jqUnit ){ ok( 4 ); this.yep( 5 ) } } );
Ventajas
  • Las mismas que QUnit.
  • Corrige la exótica sintaxis de QUnit aplicando un modelo más estándar.
  • Mantiene el contexto general limipio.
Desventajas
  • Añade una capa más de complejidad a los tests y precisa de más dependencias.
Conclusión

Si optamos por QUnit, jqUnit puede resultar un complemento interesante. Su compatibilidad nos permite portar tests desde otros frameworks pero conserva las limitaciones de citadas anteriormente.

JsUnit

JsUnit, iniciado en 2001, es el framework más veterano dentro de la escena Javascript. En esencia, se trata de un port de JUnit que incluye soporte para la ejecución de tests en múltiples navegadores y máquinas con distintos sistemas operativos. El proyecto era mantenido por la empresa Pivotal Labs, quienes actualmente, han adoptado Jasmine como framework de pruebas principal. El desarrollo de JsUnit se ha cancelado pero los repositorios permanecen online para forks.

Ejemplo Sintaxis
function testAddition() {
  assertEquals( "2 plus 3 is 5", 5, add( 2, 3 ) );
}
Ventajas
  • Sintaxis estándar.
  • No precisa de tecnologías extenas, todo tiene lugar en un entorno HTML.
  • Soporte para multinavegador y multiplataforma.
Desventajas
  • Proyecto oficial cancelado y descontinuado.
  • Igual que QUnit, los tests se asocian al contexto general (global namespace).
  • No permite tests de código asíncrono.
Conclusión

No existe realmente ningún motivo para adoptar este paquete. La compentencia ofrece mejores características y soporte.

YUI Test

YUI Test forma parte de la Yahoo YUI Library. Es una solución madura y perfectamente integrada con el resto de componentes YUI, lo que la hace perfecta para proyectos de gran envergadura. Auqnue no se corresponde directamente con un port de alguna especificación xUnit, comparte características con nUnit y JUnit.

Ejemplo de Sintaxis
testName : function () {
  var Assert = YAHOO.util.Assert;
 
  Assert.isObject( this.data );
  Assert.isString( this.data.name );
  Assert.areEqual( "yuitest", this.data.name );
}
Ventajas
  • Creación rápida de tests mediante una sintaxis más o menos estándar.
  • Conjunto de afirmaciones robusto.
  • Detección de errores avanzadas a través de excepciones.
  • Soporte para pruebas con código asíncrono.
  • Simulación de eventos para ratón y teclado en el DOM.
Desventajas
  • Comparte el contexto YUI.
  • Algo pesado.
  • Elabora un HTML de salida muy elaborado al modo consola, que puede complicar la lectura y usabilidad en determinados entornos.
  • Pese a que la mayor parte de la sintaxis resulta estándar, posee algunas instrucciones particulares.
Conclusión

YUI Test es una opción interesante si utilizamos las herramientas ofrecidas por Yahoo en nuestras aplicaciones. Quitando las particularidades de su sintaxis, destaca su robustez haciéndose una opción recomendable para grandes proyectos.

JsTestDriver

JsTestDriver es sin duda la solución más robusta en cuanto a frameworks de pruebas en Javascript. Utiliza un pequeño servidor para ejecutar los tests, lo cual permite manejarlo a través de la línea de comandos. Al no necesitar del navegador, se convierte en la herramienta idónea para el desarrollo de aplicaciones de escritorio o desde el lado del servidor. Además, su integración con algunos de los IDEs más populares permite resumir todo el proceso de testeo a pulsar un botón.

Ejemplo de Sintaxis
DemoTest = TestCase("Demo_Test");  
 
DemoTest.prototype.test_hello_world = function() {
  var target = new Demo();
  assertTrue( "wrong result returned", target.helloWorld() == "HelloWorld" );
});
Ventajas
  • Control por línea de comandos.
  • Ejecución de tests paralelos en múltiples navegadores en múltiples máquinas, incluidos los dispositivos móviles.
  • Control total del DOM con inyección directa de código HTML.
  • Integración con Eclipse e Intellij IDEA (de momento no funciona con PHPStorm o WebStorm).
  • No precisa de una estructura HTML para mostrar los resultados lo que se traduce en unos tests muy rápidos.
Desventajas
  • No permite una ejecución vía HTML.
  • Su instalación requiere de cierto conocimientos técnicos y dependencias.
  • El soporte para código asíncrono está en desarrollo.
Conclusión

JsTestDriver es una solución sólida con una sintaxis limpia y clara. Especialmente interesante para utilizar en sobre aplicaciones móviles, de escritorio o en grandes proyectos.

Y hasta aquí la segunda entrega de nuestra introducción sobre TDD en Javascript. En la tercera parte, revisaremos algunas de las herramientas que podemos encontrar dentro de la familia BDD (Behaviour Driven Development) así como las principales diferencias entre ambas metodologías.

Más:
Esta entrada fue publicada en Sin categoría. Guarda el enlace permanente.

Aún no tenemos debug!
Ningún marciano se ha comunicado.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *