jShoulda y JsUnitTest, pruebas unitarias en JavaScript al estilo Shoulda
Hablábamos el otro día de QUnit, hoy hablaremos de JsUnitTest –otro sistema de testing– y de jShoulda, una capa de abstracción que un servidor ha moldeado con sus propias manos y a imagen y semejanza de Shoulda.
Principiemos hablando de JsUnitTest, un port sin dependencias del sistema de testing desarrollado para probar prototype y script.aculo.us.
¿Qué necesitamos?
- jsunittest.js, la biblioteca de testing;
- un documento HTML que la referencie.
Algo como esto bastará:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Minimal JsUnitTest</title>
<script src="/js/jsunittest.js" type="text/javascript"></script>
<link rel="stylesheet" href="/css/unittest.css" type="text/css" media="screen"/>
</head>
<body>
<h1>Minimal JsUnitTest</h1>
<div id="testlog">
Los resultados de las pruebas se muestran, por defecto
en el elemento con id=testlog
</div>
<script type="text/javascript">
// aquí irá el código de nuestras pruebas,
// también podemos hacer referencia a un script externo
</script>
</body>
</html>
Cómo escribir tests con JsUnitTest
Para definir y ejecutar las pruebas, creamos una instancia de Test.Unit.Runner:
new Test.Unit.Runner({});
En el objeto de configuración que pasamos como argumento, definimos como propiedades las pruebas a realizar:
new Test.Unit.Runner({
testNombreDelTest : function() {
this.assert(true);
},
testOtroTest : function() {
this.assert(true);
}
});
Los nombres de las propiedades correspondientes a pruebas deben comenzar por “test”. Si deseamos ejecutar funciones de preparación y limpieza antes y después de cada una de las pruebas, utilizamos las propiedades setup y teardown para definirlas:
new Test.Unit.Runner({
setup : function() {
this.foo = 1;
},
testNombreDelTest : function() {
this.assert(true);
},
testFoo : function() {
this.assertEqual(1, this.foo);
}
});
Observa: tanto las funciones de preparación como las de prueba se ejecutan en el contexto de una instancia de Test.Unit.Testcase. Las posibles aserciones son métodos de dicha instancia (por eso usamos this). También podemos utilizar this para mantener variables que vayamos a utilizar en nuestras pruebas.
La mayor parte de los métodos asertivos aceptan tres parámetros: el valor esperado, el valor real y (opcionalmente) un mensaje que el sistema nos mostrará en caso de error.
He aquí una lista deliberadamente incompleta de los métodos de aserción:
- assert
- assertEqual
- assertNotEqual
- assertEnumEqual
- assertEnumNotEqual
- assertHashEqual
- assertHashNotEqual
- assertIdentical
- assertNotIdentical
- assertNull
- assertNotNull
- assertUndefined
- assertNotUndefined
Comprueban lo que su nombre parece indicar. Un detalle importante: en JavaScript, dos arrays con el mismo contenido (o dos objetos-usados-como-hashes) no pueden compararse mediante igualdad. Por tanto, si quieres comparar dos arrays necesitarás utilizar assertEnumEqual. Y para comparar dos objetos, assertHashEqual.
Automatización
drnic, el humano detrás de JsUnitTest, es también padre de newjs, un gem (si no sabes qué es un gem, ignora esta sección hasta que lo averigües) pensada para facilitar el desarrollo de bibliotecas JavaScript. Sus virtudes:
- incluye JsUnitTest;
- simplifica la creación de nuevos tests;
- automatiza la ejecución de los tests.
Sin entrar en detalles. O lo ves, o no lo ves:
$ newjs milib
create
create config
create lib
create src
create script
create tasks
create test/assets
create test/unit
create test/functional
create test/assets/unittest.css
create test/assets/jsunittest.js
# etcetera
dependency install_rubigen_scripts
exists script
create script/generate
create script/destroy
$ cd milib
$ script/generate unit_test basic
exists test/unit
create test/unit/basic_test.html
$ rake test_units
(in /Users/choan/Current/tirame/milib)
Started tests in Safari
.
Finished in 2 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
Started tests in Firefox
.
Finished in 1 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
Skipping Internet Explorer, not supported on this OS
Skipping Konqueror, not supported on this OS
Started tests in Opera
.
Finished in 3 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
Sí, amigo mío, mediante la ejecución de una tarea rake hemos ejecutado las pruebas en cada uno de los navegadores disponibles en nuestro sistema y hemos recogido los resultados. Algo que Chuck Norris no es capaz de hacer.
jShoulda
Pero vayamos con jShoulda, que yo he venido aquí a hablar de mi libro.
jShoulda es una capa de abstracción para JsUnitTest que permite escribir nuestros tests al más puro estilo Shoulda. Algo así:
context('A context', {},
should('run a test', function() {
this.assert('Yay!');
}),
)();
¿Y qué ventaja tiene esto? Para un test vulgar, ninguna, francamente. Pero si tus pruebas requieren de inicializaciones complejas, la cosa se pone chula.
context('A context', {
setup: function() {
this.foo = 1;
}
},
should('run its setup function', function() {
this.assertEqual(1, this.foo);
}),
context('which is a "nested" context', {
setup: function() {
this.foo +=1;
}
},
should('run both setup functions', function() {
this.assertEqual(2, this.foo);
})
)
)();
Para utilizar jShoulda necesitas… jshoulda.js. Y un documento HTML como el anterior, al que añadirás la referencia a la biblioteca.
Pero para probarlo no necesitas descargar nada. Pásate por la página del proyecto. Los resultados de pruebas que encontrarás en ella (suponiendo que la visites con JavaScript activado) corresponden al test de ejemplo ejecutado en vivo. Si quieres juguetear, doblecliquea en el código, modifícalo a tu gusto y pulsa el botón. Sugerencia: haz fallar las pruebas. Mola.
Si estás interesado en jShoulda y tu inglés no es ni mejor ni peor que el mío, lee el tutorial de jShoulda, suscríbete a la lista de correo y, si das con algún bug, arréglalo.