miércoles, diciembre 19, 2007

Unit Test para renderizadores de HTML

Actualmente estamos en la etapa de diseño y desarrollo de una nueva release del proyecto en el que vengo trabajando hace 4 años (uhhh, como pasa el tiempo)... si bien la release no es algo tan complicada, nos tomamos el tiempo para mejorar el componente de Vista, y cuando digo vista estoy hablando principalmente de la renderización a html. La metáfora que tenemos es que al componente de Vista solo llega un ViewBean que es la representación de la pantalla en objetos, y este componente debe a través de renderers asociados a cada elemento del ViewBean generar el HTML. Con lo cual apuntamos a que la generación de la vista sea creando objetos estándares y no tocar absolutamente nada de HTML o el lenguaje que general el html como Velocity o JSP. Pero para esto necesitamos crear buenas abstracciones de UI, y va a ser un proceso muy iterativo, dudo que pueda ser para esta release, todavía necesitamos mucha interacción con la persona que define las pantallas para que trate primero de tener una consistencia funcional. De todas maneras, exista o no una consistencia funcional, la idea de tener objetos/componentes abstracto que permitan reutilizar los renderers sigue siendo interesante.
En fin, me estoy perdiendo el foco, del post, una de las cosas que estoy tratando de hacer es crear un framework de Unit Test, para que todos los desarrolladores de renderers pueden Uni Testear los renderizadores sin necesidad de ver el resultado en el browser, con lo cual mi idea es poder, para cada test case hacer algo como:
1- Crear el ViewBean con la información que del escenario que quiero testear
2- Ejecutar el Renderizador que devuelve el HTML
3- Realizar los asserts, queriando el HTML para ver si la información se reemplazó correctamente, ver si hizo los cantidad de Loops correctos, si pasó por los caminos que yo especifiqué , etc, etc, etc. No me interesa saber si el html tiene todos los temas de estetica ya que eso lo va a tocar una persona a fin.

El problema que tengo hoy en día es que no encuentro un framework/componente que me permita llegar al paso tres, ya que al HTML llego sin problema, lo que necesito ahora es buscar una forma de inspeccionar el HTML en texto que tengo y poder consultarlo para hacer los assert correspondientes. Se me ocurrieron tres ideas :
  • Usar HtmlUnit. Pareciera la solución más obvia, pero investigando un poco el framework me di cuenta que no es tán feliz como yo pensaba. Si o si para consultar un HTML tenes que hacer un pedido por la red y http, cosa bastante poco práctica. Obviamente esto se puede evitar mockeando el conector y devolviendo siempre un HTML fijo. El problema es que estos componentes que yo quiero unitestear solo me devuelven una porción de HTML y no un HTML bien formado con el head y el body, con lo cual es imposible llegar a tener una representación objetosa del HTML que me permita consultarlo. Lo que me gustó de HTMLUnit es que tiene la representación objetosa de los elementos del HTML. Obviamente estuve muy poco tiempo viendo el framework y quizá se pueda, pero en fin... me hizo sentir un poco infeliz el HtmlUnit, aparte de haber muy poca documentación.
  • Usar Expresiones Regulares, en principio me pareció que era una idea copada, pero despues pensé en el resto, si a la gente ya le cuesta hacer un Test Case de codigo de Modelo o Controller, o sea Java puro, también le pido que haga un Test Unitario para los renderizadores, sumado a esto, aprender expresiones regulares que entiendan el HTML para verificarlo, me recibo de necio. Digamos que las Expresiones Regulares son algo duras de entender, incluso las que creó uno mismo... con lo cual solución descartada...
  • La ultima, que es la que voy a probar es usar XPath, ya que en teoría cada HTML renderizado por cada renderer debería ser un XML bien formado, con lo cual eso me permitiría hacer queries con XPath y tener assert mucho más sencillos. Obviamente la gente que haga estos test cases tiene que aprender Xpath, lo cual no es algo trivial, pero bue por ahora es lo mejor que se me ocurre.
Hace 4 años estuve viendo jameleon y creo que me puede servir, pero cuando entré a la página me asustó un poco, alguno tuvo alguna experiencia con esto? creen que me pude servir?

En cuanto tenga algún resultado les voy a contar...

1 comentario:

Javier Fernandes dijo...

Hola Gustavo! qué tal?
Muy interesante la problemática que planteas.

En el proyecto anterior en el que estuve efectivametne nos encontramos con este tema.
Uff, tengo que explicar bastante del proyecto para que se entienda el contexto y lo "poco" que pudimos hacer al respecto ya que fue al final del mismo que le pudimos dedicar un poquito de tiempo.

Para la capa de presentación, mi empleador le habia vendido al cliente un "framework" web que habia desarrollado una persona para otros proyectos de otros clientes.
Esto fue por el 2002/2003. Todavía no había mucho más que struts.

A lo largo de los 2 años y medio, casi tres, que estuve en presentación, con el arquitecto (Nico Passerini) y otro flaco, dimos vuelta completamente el framework llevandolo de a poco, de un modelo de controles "a la swing" a un modelo mvc, más domain-oriented con mecanismos de inferencia y metadata de dominio etc.

Lo malo fue, que desde el principio nunca tuvimos tests de ese framework. Y tristemente debo admitir que nos dormimos a la hora de poner un punto final a eso y empezar a testear de una vez.
En parte pasaba que era romper la incercia, y en parte, al laburar sobre todo un fwk que no conoces cuesta muchísimo más ver como podes ir testeandolo unitariamente.
Por suerte compensamos la falta de tests con un buen diseño y código prolijo, que refactorizabamos continua y frenéticamente para mantener las buenas prácticas como el "once and only once" (o también conocido como DRY "don't repeat yourself").

Esto hacía mucho mas fácil el mantenimiento y el arreglo de los bugs que aparecían de vez en cuando De hecho, ahora que comparo ese número de bugs con los que manejamos en mi laburo actual veo que era casi infimo. Y eso que el actual es un producto estrella de una empresa mundialmente conocida. Pero tampoco tienen tests, con lo cual se podría hacer un lindo estudio/analisis del impacto de un buen diseño sobre el número de bugs.. je!

Volviendo, tambien desde un principio, el fwk de presentacion tenia por encima una arquitectura piola. Era el concepto de renderering del modelo.
La idea era, como vos decis que te daba una forma declarativa de asociar "renderers" a modelos. Era bastante generico y abajo teniamos impl de renderers a html mediante EL (ExpressionLanguage), pero también existían renderers JSP, renderers con velocity, renderers custom de xmls que se procesaban, renderers a XUL, etc.

Bueno trato de cerrar un poco la idea, los renderers eran "pedacitos" de html con expressions, con lo cual, como vos, no teniamos un único html de la página (aunque si querías lo podías hacer), sino que teniamos tanta granularidad como quisieramos: por caso de uso, por formulario, por control, etc.

En un momento del proyecto se habia tomado la decisión de usar una herramienta tipo bot para hacer tests usando un api que levantaba un iexplorer y lo manipulaba directamente a traves de un bridge COM. Como advertí, y sigo creyendo, ese tipo de tests en un 90% de los casos no merece el esfuerzo que se le dedica. Y pasó que terminó siendo un "fwk" de tests en sí, que requería mucho laburo encima. Ademas los tests no terminaban siendo unitarios y al pegarle al html directo la descripcion de los errores era criptografica.

Estas medidos fueron haciendo que le esquivemos al testeo de verdad. Si bien por el final parte del modelo tenia tests. (Pensá que nuestro modelo para rendering no era todavia el negocio sino que era un modelado de los controles de los formularios, casos de uso, etc, agnóstico de la tecnologia en que se renderizaria -html, XUL, etc-. Asi que basicamente testeabamos los controles sin involucrar html en el medio.)

Un día me cansaron bastante estos tests y decidí dar al menos un pasito en la direccion que creia correcta. Entonces hice un abstract test bastante "precario" que usaba el fwk de rendering para renderinzar un modelo dummy y comparaba el resultado del rendering con un html "esperado", que leía infiriendolo por el nombre del test.
Los tests tenian una interfaz especifica que le permitian a un procesito genererar ese html "esperado/resultado" la primera vez.

Asi pasamos a tener tests especificos y unitarios de rendering, separados de los tests de los controles.
Lo bueno de esto es que haciamos un solo test del renderer del combo por ejemplo, y si fallaba ese te evitabas de rastrear el error en un test de aceptacion de los bots.

Si bien eran muy precarios porque comparaban los strings del html, me parecía en su momento que era un lindo punto de partida.

Uh, este post se puso muy largo :(
Lo cierro con una mini conlusión.

Me gusta la idea de testear el rendering. No me gusta la idea de que hace falta testear rendering para poder testear como está funcionando la aplicacion. Creo, como me pareció expresar más arriba, que la "solución" viene por el lado de que la arquitectura del proyecto tenga cierta separación de "incumbencias", donde la estabilidad y la seguridad te vienen dadas por la sumatoria de tests de diversa índole que testean diferentes aspectos. Ergo, los tests de rendering gralmente no deberían ser muy rebuscados y solo validarían pequeñas variaciones estéticas del html que no puedas testear en un test agnostico de la web, es decir en un test de dominio o de framework desacoplado del html.

Bueno.. te maté con un post larguísimo, perdón.
Espero que al menos te resulte entretenido, aunque no te di una respuesta concreta =P
Saludos!

PD: xpath suena como una buena idea para declarar asserts sobre los htmls. Probablemente si seguía en el proyecto lo hubiera usado para extender los tests.