viernes, 3 de mayo de 2013

Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon

Jasmine
Backbone.js
Tener un conjunto de pruebas unitarias sobre el código de la aplicación que desarrollamos es importante para tener más seguridad de que lo que hacemos funciona como se espera y no introducimos errores en funcionalidades que ya existían al hacer modificaciones, además de que en el caso de que haya errores estos serán más fácilmente descubiertos y resueltos en menos tiempo lo que hará que seamos más productivos. Si en una aplicación web es posible que ya hagamos pruebas unitarias sobre el código del lado del servidor, ¿por que no hacerlo también en el lado del cliente? Si la aplicación tiene un peso importante de javascript en el lado del cliente, y esa es la tendencia, es recomendable tener también teses unitarios de esta parte del código.

Aprovechando el ejemplo que hice de una aplicación bastante completa de una lista de tareas que utilizaba Backbone, RequireJS, Mustache en el lado cliente y Tapestry y RESTEasy en el lado del servidor ahora le añadiré al código javascript el conjunto de pruebas unitarias con Jasmine y Sinon.

Jasmine es una herramienta para realizar pruebas basadas en BDD (behavior-driven development). BDD es parte de las metododologías TDD (test-driven development) haciendo énfasis en que la gente del dominio pueda trabajar con la gente técnica. Jasmine nos servirá para hacer las pruebas unitarias, Sinon nos permitirá usar spies, stubs y mocks en esas pruebas:
  • Spy: Un spy es una función que se envía como parámetro al sujeto bajo prueba para que recolecte datos sobre lo que sucede dentro de esa función y objeto, se llama espía porque se envía tras la lineas enemigas para que recolecte información. La información que puede recolectar es el número de veces que se llama una función, con que parámetros y cuales, los valores de retorno o si lanzó excepciones.
  • Stub: ofrecen un comportamiento parcial preprogramado del objeto real, se puede utilizar para que proporcione los datos que queramos al sujeto bajo prueba. Los stubs también pueden ejercer de espías.
  • Mock: que también son espías (funciones falsas) y stubs (ofrecen comportmiento preprogramado) conocen las expectativas de como han de ser usados, requiriendo que se llamen con ciertos parámetros, un número de veces, en cierto orden, .... Conocen cual es el comportamiento esperado del sujeto bajo prueba y una vez hecha la prueba se puede comprobar.
A continuación veremos el código de las pruebas para la aplicación, las pruebas se centrarán en el código en el código de la lista de tareas y de probar las diferentes partes por separado, el modelo, vista y controlador. Veamos primeramente las pruebas para los modelos Tarea y Tareas, las pruebas se centrarán en las funciones adicionales que hemos asociado a los modelos, es decir, probaran lo que hemos hecho no Backbone.

En la parte del controlador de la vista nos centraremos en probar los métodos y eventos como onChangeCompletada en TareaView y addTarea, resetTareas, onClickLimpiar y onKeypressNuevaTarea en TareasView. Dado que la lista de tareas hace uso de un servicio REST utilizaremos Sinon para atrapar esas peticiones AJAX que se harían en la realidad.

En la vista probaremos que dado un modelo el resultado del html contiene los elementos que esperamos:

Las pruebas se lanzan abriendo la página html SpecRunner.html en el navegador. Esa página html tiene la dependencia sobre RequireJS y este carga en el navegador el resto de dependencias que necesiten los diferentes módulos incluido el módulo bajo prueba de la lista de tareas. Las pruebas de Jasmine se definen en otro módulo de RequireJS que tiene como dependencia la librería de Jasmine, Sinon y por supuesto el módulo tareas que probará. El resultado es el siguiente:
Backbone se presta muy bien a las pruebas unitarias. Una gran característica es que para realizarlas no es necesario que las vistas se asocien a un DOM real, funcionan en memoria, de modo que no hace falta insertar ni quitar contenido de la página mientras se van ejecutando los teses, no hay necesidad de limpiar el DOM para los siguiente teses y así funciona más rápido. Como usa el patrón MVC cada una de las partes del modelo, vista y controlador pueden probarse por separado. Este ejemplo no muestra todas la posibilidades de Jasmine y Sinon pero da una idea de como son las pruebas con ellas.

En mi repositorio de GitHub puedes encontrar el código fuente completo del ejemplo de pruebas unitarias en javascript con Jasmine y Sinon.

Y con esta es por el momento la última entrada sobre javascript que escriba de esta serie, no descarto escribir alguna más sobre javascript en el futuro porque alrededor de este lenguaje de programación están surgiendo muchas utilidades y librerías.

Referencia:
Introducción y ejemplo de RequireJS
Introducción y ejemplo de Mustache
Logging en Javascript con log4javascript
Capturar errores de Javascript
Optimizar módulos de RequireJS y archivos Javascript
Patrón de diseño MVC del lado cliente con Backbone.js
Introducción y ejemplo de Backbone.js