3. Tweet This Book!
Please help david rene comba lareu by spreading the word
about this book on Twitter!
The suggested hashtag for this book is #DevNod .
Find out what other people are saying about the book by
clicking on this link to search for this hashtag on Twitter:
https://twitter.com/search/#DevNod
4. Índice general
Introducción 1
Automatización de unit testing 2
Asserting Results 6
Llamadas de retorno sincronicas 8
Llamadas de retorno asincronicas 10
Testeando acciones de usuario 12
Manteniendo los tests atómicos 15
Agrupando tests 18
Desarrollo Eficiente 20
5. Introducción
El testeo automático es fundamental en el desarrollo de
software. Unit tests son los bloques básicos para armar tests
automáticos: cada componente, la unidad, del programa
es acompañado por un test que puede ser ejecutado por
un test runner una y otra vez sin intervención humana.
En otras palabras, puedes escribir un test una sola vez y
ejecutarlo las veces que sean necesarias sin ningún tipo de
costo adicional. En adición a los beneficios de una buena
cobertura de testeo, esto también puede definir el diseño de
software, tambien conocido como test-driven design, donde
el test es escrito antes de la implementación. Puedes escribir
un test muy simple, verificar que falla (por que el código a
testear todavía no existe) y entonces escribir la implemen-
tación hasta que pase el test.Una vez que eso sucede, puedes
extender el test para cubrir mas funcionalidades deseadas
e implementar otra vez.Repitiendo estos pasos, el código
resultante es muy diferente al que se obtiene si se empezara
por la implementación. Unit testing en JavaScript no es
muy diferente de otros lenguajes de programación. Lo que
necesitas es un pequeño framework que provea un entorno
ejecutor de tests (test runner), ademas de algunas utilidades
para escribir los tests.
1
6. Automatización de unit
testing
Problema
Si quieres automatizar el testeo de tus aplicaciones y
frameworks, o tal vez beneficiarse del test-driven design,
escribir tu propio testing framework puede ser tentador,
pero demanda muchísimo trabajo cubrir todos los detalles
y requerimientos especiales de JavaScript para testear el
código en varios navegadores.
Solución
Aunque hay otros unit testing frameworks para Ja-
vaScript, has decidido explorar Qunit. Qunit es el unit
test framework de jQuery y es usado en una variedad de
proyectos. Para usar Qunit, solo debes incluir 2 archivos de
Qunit en tu pagina HTML. Qunit consiste de qunit.js, el
ejecutor de tests y test framework y qunit.css, que son los
estilos usados para mostrar los resultados de los tests en la
pagina.
2
7. Automatización de unit testing 3
Abrir este archivo en el navegador da como resultado
lo siguiente:
El único código de maquetado necesario en el <body>
es un <div> con id=”qunit-fixture”. Esto es necesario para
todos los tests de QUnit, aun cuando el elemento mismo
esta vacío. Esto provee el un lugar fijo para los tests, que
será explicado en la sección llamada “Manteniendo los tests
atómicos”.
La parte interesante es el elemento *
8. Automatización de unit testing 4
Discusión
El encabezado de la test suite muestra el titulo de la
pagina, una barra verde cuando todos los tests han pasado
(una barra roja se muestra cuando alguno de ellos ha
fallado), una barra con unos checkboxes para filtrar los
resultados y una barra azul con el navegador, el userAgent
(muy util para sacar capturas de pantalla de los resultados
en diferentes navegadores). De los checboxes, “Hide passed
tests” es útil cuando se ejecutan muchos tests y solo algunos
han fallado. Tildando la caja, se van a esconder todos los
tests que hallan pasado, haciendo mas fácil el enfocarse
en aquellos que fallaron (esto se explica en detalle en la
sección “Desarrollo eficiente”). Tildar “Check for globals”
causa que QUnit haga una lista de todas las propiedades
del objeto ventana (window object) antes y después de cada
test, y chequear las diferencias después de cada uno.Esto
ayuda a estar seguro de que tu código de testeo y el
código dentro de este, no exporte ninguna propiedad al
ámbito global. El “No try-catch” checkbox le dice a QUnit
de probar el código por fuera de un bloque try-catch.
Cuando el código tire una excepción, el ejecutor de tests
morirá. siendo incapaz de continuar pero se consigue como
resultado una excepción “nativa”, que puede ser de gran
ayuda en navegadores viejos donde el soporte para manejar
excepciones es deficiente (como en internet explorer 6).
Debajo de la cabecera esta el sumario, mostrando el tiempo
total que tomo ejecutar todos los tests como también el total
de tests que han pasado y fallado. Mientras todavía existan
tests que se estén ejecutando, se mostraran sus nombres.
9. Automatización de unit testing 5
El contenido actual de la pagina van a ser los resultados de
los tests, cada entrada en la lista empieza con el nombre del
test, seguido por, en paréntesis, el numero de tests que han
pasado, que han fallado y el numero total de assertions.
Clickeando cada entrada va a mostrar los resultados de
cada assertion, usualmente con detalles sobre el resultado
esperado y el obtenido. El link “Rerun” al fondo de cada test
se usa para re-ejecutar ese test únicamente.
10. Asserting Results
Problema
Un elemento esencial de cualquier unit test son las
assertions. El autor del test debe expresar el resultado
esperado y hacer que el unit testing framework lo compare
con lo valores que la implementación produce.
Solución
QUnit provee de 3 assertions.
ok( truthy [, message ] )
El mas básico es ok() que solo requiere de 1 argumento.
Si la evaluación del argumento resulta positiva (true) la
assertion pasa, si en cambio resulta con otro resultado, falla.
También acepta como parámetro opcional un texto para
mostrar en los resultados del test:
equal( actual, expected [, message ] )
La assertion equal usa el operador simple de compa-
ración (==) para comparar los resultados actuales con los
esperados. Cuando son iguales, la assertion pasa, de otra
manera, falla. Cuando falla, tanto el resultado esperado
6
11. Asserting Results 7
com el recibido son mostrados en los resultados del test,
ademas del mensaje suministrado como tercer argumento.
![]images/img4.jpg)
Comparado con ok(), equal() hace mucho mas fácil
depurar tests que han fallado, por que es obvio que valor
causo que el test fallara.Si fuera necesario una comparación
estricta (==), se puede usar strictEqual().
deepEqual( actual, expected [, message ] )
La assertion deepEqual() puede usarse como equal() y
es la mejor opción en la mayoría de las ocasiones. En vez de
usar el operador de comparación simple (==), este usa un
operador mas especifico (===). De esta forma, undefined
no equivale a null, 0 o una cadena vacía (“ “). También
compara el contenido de los objetos, así {key: value} es
igual a {key: value} cuando se comparan 2 objetos con
identidades diferentes. deepEqual() también maneja NaN,
dates, expresiones regulares, arrays, y funciones, mientras
equal() solo chequea la identidad del objeto:
En el caso de que explícitamente no se quiera comparar
el contenido de 2 valores, todavía puede usarse equal(). En
general, deepEqual(), es la mejor opción.
12. Llamadas de retorno
sincronicas
Problema
Ocasionalmente, diferentes circunstancias en tu código
puede provocar que las assertions nunca sean llamadas,
causando de que el test falle silenciosamente.
Solución
QUnit provee una assertion especial para definir la
cantidad de assertions que contiene un test. Cuando el test
se completa sin el numero adecuado de assertions, este
fallará, sin importar el resultado de los otros assertions. El
uso es muy simple, solamente llama a expect() a principio
del test, con el numero de assertions esperadas como único
argumento:
Opcionalmente, la cantidad de assertions esperadas
puede pasarse como segundo parámetro de test():
8
13. Llamadas de retorno sincronicas 9
Ejemplo practico:
Discusion
expect() provee de la mayor herramienta al probar
llamadas de retorno. Cuando todo el código corre en el
marco de la función de test, expect() no provee ningún valor
adicional - cualquier error que no permita las assertions
ejecutarse, va a causar que el test falle, por que el ejecutor
de tests captura el error y hace fallar la unidad.
14. Llamadas de retorno
asincronicas
Problema
Mientras que expect() es util para probar código sin-
crónico, este falla cuando existe código asincrónico. Código
asincrónico entra en conflicto en la forma que el ejecutor
de tests lo pone en una cola de ejecución y ejecuta los tests.
Cuando el código dentro del test framework comienza un
timeout, un interval o un ajax request, el ejecutor del test va
a continuar con el resto del test, y todos los otros test que
le sigan, en vez de esperar por el resultado de la función
asincrónica.
Solución
En vez de envolver las assertions en un test(), debe
usarse asyncTest() y llamar a start() cuando el bloque de
testeo esta completo y listo para resumir:
Ejemplo practico:
10
16. Testeando acciones de
usuario
Problemas
Código que depende de acciones iniciadas por el usua-
rio no pueden ser testeadas simplemente llamando a una
función. Usualmente una función anónima esta adherida
a un evento de un elemento Ej: un click, que debe ser
simulado.
Solución
Puedes disparar el evento usando la función de jQuery
trigger() y probar que se produzca el comportamiento espe-
rado. Si no quieres que eventos nativos del navegador sean
disparados, puedes usar triggerHandler() para solo ejecutar
los event handlers. Esto es útil cuando se testea un evento de
click, donde trigger() va a causar que el navegador cambie
de locación, que es difícilmente lo que se quiere en un test.
Asumamos que tenemos un simple registrador de teclas que
queremos probar:
12
17. Testeando acciones de usuario 13
Podemos manualmente disparar el evento keypress
para ver si el registrador esta funcionando:
Discusión
Si tu manejador de evento no depende de ninguna pro-
piedad especifica del evento, puede simplemente llamar a
.trigger(eventType). Sin embargo, si tu manejador depende
de alguna propiedad especifica del evento, es necesario
crear un objeto de evento usando $.Event y setear las pro-
piedades necesarias, como fue mostrado anteriormente. Es
tambien importante disparar todos los eventos relevantes
para comportamientos complejos como el arrastre, que
requiere de un mousedown, por lo menos de un mousemove
y un mouseup . Hay que tener presente que algunos tests
que parecen simples, son en realidad, complejos Ej: un click
es en realidad un mousedown, un mouseup y un click. Si
verdaderamente es necesario disparar todos estos eventos
depende del código que este bajo testeo. Disparar solamente
un click es suficiente en la mayoría de los casos. Si eso no
es suficiente, existen unas cuantas opciones en frameworks
18. Testeando acciones de usuario 14
para simular eventos del usuario:
*syn: “Es una librería sintética de eventos que bá-
sicamente maneja tipeo, clickeo, movimiento, y arrastre
exactamente como un usuario real haría esas acciones”.
Usada por FuncUnit, que esta basado en QUnit, para testeo
funcional de aplicaciones web. *JSRobot: “Una herramien-
ta de testeo para aplicaciones web que puede generar
keystrokes reales mas que simplemente simular el disparo
de eventos en Javascript. Esto habilita a que se disparen
comportamientos inherentes en el navegador que de otra
manera no seria posible. *“DOH Robot” provee una API
que permite a los testers automatizar los UI tests usando
real, cross-platform, inputs a nivel sistema.Esto te permite
estar lo mas cerca posible de eventos “reales” de navegador,
pero usa java applets para generar esto.
19. Manteniendo los tests
atómicos
Problema
Cuando los tests son puestos juntos, es posible tener
tests que deberían pasar pero fallan o tests que deberían
fallar pero pasan. Esto es resultado de tests teniendo resul-
tados inválidos por efectos secundarios de tests anteriores:
El primer append() añade un <div> que el segundo
equal() no toma en consideración.
Solución
Usa el método test() para mantener los tests atómicos,
siendo cuidadoso de mantener cada assertion limpia de
cualquier efecto secundario. Debes solo depender en un
maquetado fijo, dentro del elemento #qunit-fixture. Modi-
ficando y dependiendo de cualquier otra cosa puede tener
efectos secundarios:
15
20. Manteniendo los tests atómicos 16
QUnit va a resetear los elementos dentro de #qunit-
fixture después de cada test, eliminando cualquier evento
que existiera. Si usas solo elementos con esta estructura,
no necesitas limpiar manualmente para mantener los tests
atómicos.
Discusión
En adición a #qunit-fixture y los filtros explicados en
la sección llamada “Desarrollo Eficiente”, QUnit también
ofrece un parámetro llamado ?noglobals como en el si-
guiente ejemplo:
En un test normal, esto pasaría como un resultado
valido. Correr el ok() con el parámetro noglobals causa
que el test falle, por que QUnit detecta que ha populado
el objeto window. No es necesario usar este parámetro
21. Manteniendo los tests atómicos 17
todo el tiempo, pero puede ser útil para detectar polución
dentro del namespace global que puede ser problemático
en combinación con librerías de terceros. También ayuda a
detectar bugs en tests causados por efectos secundarios.
22. Agrupando tests
Problema
Has separado todos tus tests para mantenerlos atómicos
y libre de efectos secundarios, pero quieres mantenerlos
lógicamente organizados y ser capaz de correr un grupo
específicos de tests por si solo.
Solución
Puedes usar la función module() para agrupar tests:
El test que ocurre después de la llamada a module() va
a ser agrupada en ese modulo. El nombre de los tests va
a preceder por el nombre del modulo en los resultados del
test. Puedes usar ese nombre de modulo para seleccionar
los tests a ejecutar (mira la sección “Desarrollo Eficiente”).
Discusión
Ademas de agrupar tests, module() puede ser usado pa-
ra extraer código en común de tests que estén en el mismo
18
23. Agrupando tests 19
modulo. La función module() usa el segundo parámetro
opcional para definir las funciones a correr antes y después
de cada test dentro de ese modulo:
Puedes especificar el parámetro setup o teardown o
los 2.Llamar a module() otra vez sin ningún argumento
adicional va a simplemente resetear cualquier propiedad
setup/teardown definida anteriormente en otro modulo.
24. Desarrollo Eficiente
Problema
Una vez que tu set de tests tarda mas que unos segundos
en correr, quieres evadir perder un montón de tiempo
esperando los resultados.
Solución
QUnit tiene muchas características que puede solu-
cionar eso.La mas interesante solo requiere de un click
para ser activada. Clickea el ítem “Hide passed tests” al
principio de la pagina y QUnit solo va a mostrar los tests
que fallaron. Eso solo no hace diferencia en velocidad,
pero ayuda a concentrarse en los tests que fallan. Se pone
mas interesante si se toma en cuenta otra característica de
QUnit, que esta habilitada por default y normalmente no se
nota. Cualquier test que falla, QUnit guarda el nombre del
test en sessionStorage. La próxima vez que corras la serie
de tests, ese test que fallaba va a correr antes que todos los
otros tests. El orden de salida no es afectado, solo el orden
de ejecución. En combinación con “Hide passed tests” vas
a poder ver el test que fallo al principio y lo antes posible.
Discusión
El re-ordenado automático sucede por default. Esto
implica que tus tests deben ser atómicos, como se discutió
previamente. Si tus tests no lo son, vas a obtener errores
aleatorios. Arreglar esto es usualmente la mejor opción,
20
25. Desarrollo Eficiente 21
pero si estas verdaderamente desesperado, se puede setear
QUnit.config.reorder = false. Ademas del re-ordenamiento
automático, hay algunas opciones manuales disponibles.
Puedes volver a ejecutar cualquier test clickeando “Rerun”
en cualquier test. Eso va a agregar un “testNumber=N”
parámetro a la url del test, donde N es el numero de test que
clickeaste. Puedes recargar la pagina para seguir corriendo
ese test o usar el botón “Atrás” del navegador para volver
a ejecutar todos los tests. Correr todos los tests con un
modulo funciona de la misma manera, solo que eliges el
modulo a correr desde el selector al principio de la pagina
a la derecha. Va a setear un “module=N” en la url, donde N
es el nombre encodeado del modulo.
Conclusion
Esta es una introduccion basica a QUnit, con las funcio-
nes mas importantes y mas usadas de este test framework.
El contenido de este libro fue traducido al español a partir
de lo publicado en la pagina de QUnit bajo la seccion “Co-
okBook” (http://qunitjs.com/cookbook/¹) por las siguientes
personas y con la siguiente licencia:
This section was first published, under a non-exclusive
license, as the last chapter in the jQuery Cookbook, autho-
red by Scott González and Jörn Zaefferer. As QUnit chan-
ged since the book was printed, this version is more up-
to-date. This work is licensed under a Creative Commons
Attribution 3.0 Unported License.
¹http://qunitjs.com/cookbook/
26. Desarrollo Eficiente 22
Como la version original, esta traduccion posee la
misma licencia anteriormente mencionada.
La traduccion fue echa por David Rene Comba Lareu,
para DevNod.com (http://www.devnod.com²) con la unica
intencion de brindar material tecnico en español.
²http://www.devnod.com