

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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe('modelo tarea', function() { | |
it('no completado', function() { | |
var t = tareaNoCompletada.toPlantilla(); | |
expect(t.descripcion).toEqual('Tarea'); | |
expect(t.completada).toBeFalsy(); | |
expect(t.attrs.checked).toBeNull(); | |
expect(t.attrs.completada).toBeNull(); | |
}); | |
it('completado', function() { | |
var t = tareaCompletada.toPlantilla(); | |
expect(t.descripcion).toEqual('Tarea'); | |
expect(t.completada).toBeTruthy(); | |
expect(t.attrs.checked).toEqual('checked'); | |
expect(t.attrs.completada).toEqual('completada'); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe('modelo tareas', function() { | |
var modelos; | |
beforeEach(function() { | |
modelos = []; | |
modelos.push(tareaNoCompletada); | |
modelos.push(tareaCompletada); | |
}); | |
it('búsqueda completadas', function() { | |
var modelo = new tareas.Tareas(); | |
modelo.reset(modelos); | |
expect(1).toEqual(modelo.findCompletadas().length); | |
}); | |
it('eliminar completadas', function() { | |
var modelo = new tareas.Tareas(); | |
modelo.reset(modelos); | |
modelo.removeCompletadas(); | |
expect(1).toEqual(modelo.length); | |
expect(0).toEqual(modelo.findCompletadas().length); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe('tareas', function() { | |
it('marcar como completada', function() { | |
var vista = new tareas.TareaView({model: tareaNoCompletada}); | |
var input = $("input[name='completada']", vista.el); | |
input.attr('checked', true); | |
input.trigger('change'); | |
expect(tareaNoCompletada.get('completada')).toBeTruthy(); | |
expect(1).toEqual(requests.length); | |
}); | |
it('marcar como no completada', function() { | |
var vista = new tareas.TareaView({model: tareaCompletada}); | |
var input = $("input[name='completada']", vista.el); | |
input.attr('checked', false); | |
input.trigger('change'); | |
expect(tareaCompletada.get('completada')).toBeFalsy(); | |
expect(1).toEqual(requests.length); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe('tareasapp', function() { | |
var vista; | |
beforeEach(function() { | |
vista = new tareas.TareasApp(); | |
}); | |
it('inicializar lista tareas', function() { | |
vista.resetTareas([tareaNoCompletada, tareaCompletada]); | |
expect(2).toEqual($('#lista-tareas', vista.el).children().length); | |
expect('1 tareas de 2 completadas').toEqual(trim($('#estado', vista.el).html())); | |
}); | |
it('añadir una tarea', function() { | |
vista.addTarea(tareaNoCompletada); | |
expect(1).toEqual($('#lista-tareas', vista.el).children().length); | |
}); | |
it('nueva tarea', function() { | |
var input = $("input[name='nuevaTarea']", vista.el); | |
var e = $.Event('keypress'); | |
e.which = 13; | |
input.val('Tarea'); | |
input.trigger(e); | |
expect(1).toEqual($('#lista-tareas', vista.el).children().length); | |
expect('').toEqual(input.val()); | |
expect(1).toEqual(server.requests.length); | |
}); | |
it('limpiar tareas completadas', function() { | |
vista.resetTareas([tareaNoCompletada, tareaCompletada]); | |
var input = $("input[name='limpiar']", vista.el); | |
input.trigger('click'); | |
expect('disabled').toEqual(input.attr('disabled')); | |
expect(1).toEqual($('#lista-tareas', vista.el).children().length); | |
}); | |
it('botón limpiar con tareas completadas', function() { | |
vista.resetTareas([tareaCompletada]); | |
var input = $("input[name='limpiar']", vista.el); | |
expect(input.attr('disabled')).toBeUndefined(); | |
}); | |
it('botón limpiar con tareas no completadas', function() { | |
vista.resetTareas([tareaNoCompletada]); | |
var input = $("input[name='limpiar']", vista.el); | |
expect('disabled').toEqual(input.attr('disabled')); | |
}); | |
it('botón limpiar sin tareas', function() { | |
var input = $("input[name='limpiar']", vista.el); | |
expect('disabled').toEqual(input.attr('disabled')); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script type="text/javascript"> | |
require(['jasmine-html', 'specs/main-specs'], function(jasmine, specs) { | |
var env = jasmine.getEnv(); | |
var reporter = new jasmine.HtmlReporter(); | |
env.specFilter = function(spec) { | |
return reporter.specFilter(spec); | |
}; | |
env.updateInterval = 1000; | |
env.addReporter(reporter); | |
env.execute(); | |
}); | |
</script> |
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
Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry
http://tinnedfruit.com/2011/03/03/testing-backbone-apps-with-jasmine-sinon.html
http://blog.involver.com/2012/01/26/testing-backbone-js-best-practices-2/
http://kilon.org/blog/2012/08/testing-backbone-requirejs-applications-with-jasmine/
Backbone.js
RequireJS
Mustache
Jasmine
Sinon
RESTEasy
Apache Tapestry
http://tinnedfruit.com/2011/03/03/testing-backbone-apps-with-jasmine-sinon.html
http://blog.involver.com/2012/01/26/testing-backbone-js-best-practices-2/
http://kilon.org/blog/2012/08/testing-backbone-requirejs-applications-with-jasmine/
Backbone.js
RequireJS
Mustache
Jasmine
Sinon
RESTEasy
Apache Tapestry