Omitir navegación.
Principal

jferrer's blog

Pruebas unitarias y de persistencia en Ruby on Rails


Pruebas

Uno de los aspectos que me ha sorprendido más gratamente de Ruby on Rails es la facilidad para hacer pruebas de una aplicación web a todos los niveles.

En el entorno J2EE no he acabado de encontrar herramientas de pruebas que cubrieran todas las necesidades del desarrollo de aplicaciones web. Para pruebas unitarias JUnit o sus competidores están muy bien, pero cuando te sales de ese terreno las cosas no están tan claras. En particular los entornos para llevar a cabo pruebas funcionales provocan que estas supongan un coste considerable.

En Rails tenemos el equivalente a JUnit+dbUnit+HttpUnit+Cactus+Jameleon y todo ello perfectamente integrado. Así da gusto :) El único pero que le pongo son los nombres que han dado a los distintos tipos de pruebas y que resultan bastante engañosos.

En esta entrada del blog comienzo explicando las pruebas que Rails denomina unitarias y que incluyen tanto a las realmente unitarias como a las pruebas de las clases de modelo Active Record. Estas últimas no son puramente unitarias, dado que dependen de que esté disponible una base de datos con determinada información en ella.

Pruebas de presistencia con Fixtures

Las primeras no tienen ningún misterio para las que hayan usado JUnit así que centrémonos en las segundas. Para ellas, Rails proporciona el sistema de Fixtures que permite definir los datos que deben estar presentes en la base de datos antes de la ejecución de la prueba. La forma de definir estos datos es mediante un fichero con formato YAML por cada clase del modelo. Todos estos ficheros se almacenan en el directorio tests/fixtures. Por ejemplo en SprintTracker los ficheros tests/fixtures/sprints.yml contiene:

La hora de la verdad con Ruby on Rails


Experiencias e informes

La versión 0.1 de SprintTracker contenía únicamente funcionalidades típicas de los artículos de Rails. La hora de la verdad ha llegado al proponerme implementar las funcionalidades que había sugerido para la versión 0.2 y 0.3. Las más importantes son:

  • Gráficas Burndown de los sprints
  • Reordenación jerárquica de las páginas de administración de proyectos, sprints y tareas. Esto ha permitido mejorar mucho la usabilidad de SprintTracker
  • Páginas de edición rápida para añadir todas las reestimaciones de un día concreto o para una tarea concreta. Esto es fundamental si, como estoy haciendo yo últimamente, usas una hoja excel para llevar el seguimiento del proyecto
  • Otras mejoras menores: borrado en cascada de proyectos, sprints y tareas, validación a medida de reestimaciones (no puede haber dos para la misma tarea y día), etc.

En la captura de pantalla se muestra la vista de un sprint en la que se aprecian varias de estas funcionalidades.


Tras implementar estas funconalidades he liberado SprintTracker v0.3 y creo que ya empieza a ser una aplicación lista para ser usada. Para ellas no había ningún artículo que me guiara y ello me ha obligado a aprender mucho más sobre Rails y sobre todo a manejarme con las referencias online imprescindibles para todo desarrollador de Rails:

Peculiaridades de Ruby para desarrolladores Java (parte 2)


Experiencias e informes

Continuando con la entrada anterior sigo explorando los aspectos de Ruby mas peculiares para un programador que viene del mundo Java.

Closures

En los artículos de autores relacionados con el mundo de los métodos ágiles es habitual oir hablar de lenguajes que soportan closures. Pues bien, tengo que reconocer que aunque creía haber entendido bien el concepto lo cierto es que al menos no lo había asimilado. Usandolo con Rails por fin lo he conseguido, así que ahí va mi intento de explicar lo que son:

Un closure puede verse como un bloque de código que se sitúa en paralelo al del método y al que éste puede pasarle el control cuando crea conveniente usando el método yield()

Un ejemplo típico de uso podría ser un método que itera por un árbol y realiza una acción con cada nodo. En Ruby es posible pasar al método (al que llamaremos iterateTree) un fragmento de código que será el que realice la operación sobre el nodo. Por ejemplo, la invocación del método para imprimir cada nodo podría ser:

  tree.iterateTree do |node|
     puts "Iterating node: " + node
  end

El fragmento de código entre do y end recibe el nombre de closure. El método iterateTree invocará el closure usando el método especial yield:

Peculiaridades de Ruby para desarrolladores Java


Experiencias e informes

Durante estos días en que he estado aprendiendo Ruby y Rails para desarrollar SprintTracker 0.1 me he ido encontrando diferentes elementos de sintaxis que me han sorprendido, confundido o incluso disgustado (al menos inicialmente). He pensado que podía ser buen idea revisarlos porque creo que conociéndolos resulta mucho más fácil entender los fragmentos de código presentados en la mayoría de artículos sobre el tema. Como son bastantes cosas lo voy a dividir en dos entradas del blog.

Llaves y paréntesis opcionales

En Ruby los paréntesis y las llaves (() y {}) pueden omitirse cuando son obvios. Por ello en Rails es habitual encontrar invocaciones a métodos de la forma:

  nombre_método parametro

Que es equivalente a :

  nombre_metodo(parametro)

Cuando un mapa (hash) es el último parámetro de un método las llaves pueden ignorarse, así que uniendo esto a lo anterior es habitual encontrarse llamadas a métodos de la forma:

  nombre_metodo primer_parametro, :clave1 => "valor1", :clave2 => "valor2"

Que es equivalente a:

  nombre_metodo(primer_parametro, {:clave1 => "valor1", :clave2 => "valor2"})

Y que un programador Java escribiría como:

  Map segundoParametro = new HashMap();
  segundoParametro("clave1", "valor1");
  segundoParametro("clave2", "valor2");
  nombreMetodo(primerMarametro, segundoParametro);

Publicado SprintTracker v0.1


Experiencias e informes

Efectivamente SprintTracker ya está aquí empaquetada y descargable. No esperes que te resulte muy útil aún, claro, es más bien una prueba de concepto. Tras completar la funcionalidad tal y como expliqué en mi post anterior sólo queda un lavado de cara visual a la aplicación y empaquetarla para que sea fácil de probar. En la siguiente captura de pantalla puede verse el resultado final, en concreto la pantalla con el listado de proyectos dados de alta:

Os explico cuales han sido los últimos pasos para crear la versión 0.1:

Mejorando el aspecto visual y la navegación

Según iba avanzando con el desarrollo de la aplicación me había preocupado bastante poco del aspecto visual de la misma y la navegación entre sus apartados. Simplemente había dejado la apariencia que se crea por defecto al ejecutar el script scaffold. El principal inconveniente de esta es que no hay ningún mecanismo para acceder de unas herramientas a otras (y SprintTracker tiene ya 5).

Así que lo primero que pensé fue en crear un primer menú. La cuestión es ¿dónde lo pongo? Leyendo uno de los artículos que tenía a mano vi que en Rails las páginas se construyen a partir de tres componentes visuales:

  1. Layout: es el fichero que contiene la estructura de un conjunto de páginas. Se encuentran en la carpeta views/layouts
  2. View: es el fichero que contiene el contenido principal de cada página concreta. Se encuentran en subcarpetas de views con el nombre del controlador asociado a la vista.
  3. Parts: son ficheros incluidos, generalmente desde views usando el método <% render :partial => 'nombre_del_part' %>. Por convenio el primer caracter del nombre del fichero es un subrayado y la extensión es (por ejemplo _nombre_del_part.rhtml). Las parts se encuentran junto a las Views que las incluyen.

Aquí me encontré con el primer problema, para cada una de las herramientas se había generado en la carpeta view/layouts un layout diferente. Eso es un problema, porque si el menú es igual a todos ¿tengo que copiarlo a cada uno de los layouts? Eso viola el principio DRY (Don't Repeat Yourself) y por tanto no me hacía mucha gracia. Pero rápidamente leí la solución: basta con borrar todos esos layouts y crear una nuevo (copiando el contenido de cualquiera de los otros) llamado application.rhtml. Rails primero mira si hay un layout con el nombre del controlador que se ha invocado y si no lo encuentra busca siempre uno llamado application.rhtml. De esta forma creando este fichero es muy sencillo hacer que todas las páginas del portal tengan la misma apariencia, pero si hay alguna excepción se puede crear un layout para un controlador concreto.

Una vez determinado el fichero ya podía escribir el código HTML de la cabecera, el menú etc. Lo que quería era:

  • Crear una apariencia más o menos decente (no soy diseñador, no le puedo pedir peras al olmo) pero muy poco sobrecargada
  • Tener el código HTML lo más sencillo posible
  • Poder cambiar la apariencia lo más posible usando CSS (para permitir a futuros usuarios de la herramienta adaptarla)

Con estos requisitos en mente me encajaba a la perfección usar el framework CSS de Mike Stenhouse que ha sido propuesto recientemente. La idea de este framework es proponer una estructura HTML concreta con unos nombres de identificadores y estilos CSS definidos que permitan representar cualquier página web normal. Gracias a las técnicas CSS soportadas a la gran mayoría de los navegadores es posible aplicar toda la apariencia a la página desde fuera del HTML. Este framework ha tenido repercusión en el mundo Java gracias al concurso propuesto por Matt Raible para crear apariencias para él.

En resumen, decidí seguir las guías de dicho framework CSS para SprintTracker y viendo los resultados me alegro de ello. Por un lado he perdido mucho menos tiempo de lo habitual maquetando el código HTML y por otro futuros cambios en la apariencia afectarán en la mayoría de los casos sólo al código CSS. En concreto siguiendo las guías de CSS modular del propio Stenhouse he dividido los CSS en componentes. En particular uno de ellos, _color.css contiene todas las definiciones de colores con lo que modificando sólo ese es posible cambiar todos los colores usados por SprintTracker.

Empaquetamiento

Uno de los aspectos que más me preocupaba cuando pensaba en implementar SprintTracker en Ruby on Rails era la dificultad de distribuirlo para que la gente lo probara. Yo pensaba "Si lo hiciera en Java, cualquiera puede instalar un tomcat y desplegar en él un WAR, o incluso puedo distribuir un tomcat con el WAR desplegado". Sin embargo mis temores se han convertido en nuevo reconocimiento hacia la comunidad de Ruby dado que me ha sido posible incluso crear un ejecutable autocontenido con toda la aplicación. ¡Más facilidad imposible!

Las herramientas que llevan a cabo la magia necesaria son tar2rubyscript y rubyscript2exe. Para instalarlas basta con ejecutar:

$ gem install tar2rubyscript
$ gem install rubyscript2exe

Una vez instalados aprendo a usarlas tal y como se explica en el artículo Distributing Rails Applications. El artículo es bastante nuevo (de hace 10 días) pero tiene bastantes imprecisiones, quizá porque habla de una versión antigua de Rails. En definitiva los que he tenido que hacer es ir al en el que hay un subdirectorio denominado SprintTracker que contiene toda la aplicación y ejecutar:

$ tar2rubyscript SprintTracker

Con este comando genero un fichero SprintTracker.rb que contiene toda la aplicación. Basta con tener Ruby instalado para poder ejecutarlo como ruby SprintTracker.rb. Me ha recordado a empaquetar una aplicación java en un jar y ejecutarla como java -jar app.jar sin embargo con aplicaciones J2EE no es tan fácil.

Pero ruby permite ir un paso más: puede incluirse el propio ruby en el paquete y crear un ejecutable. Basta con ejecutar:

$ rubyscript2exe SprintTracker.rb

Como inconveniente valga decir que esta herramienta genera un ejecutable que sólo funciona en la plataforma en la que se empleó. En mi caso para Linux (SprintTracker_linux).

En la generación de estos ficheros me he encontrado con dos problemas:

  • No se incluía la dependencia con sqlite3 y por tanto fallaba al ejecutarlo en un ordenador que no lo tuviera instalado aparte. Creo que esto ya lo he corregido, pero agradecería al que pudiera probarlo que me lo confirmara.
  • No me ha funcionado el sistema para sacar la base de datos fuera del ejecutable. Por ello esta versión 0.1 pierde los datos una vez se cierra. No me preocupa para esta primera versión, pero tenlo en cuenta: ¡no introduzcas datos reales que se van a perder

Debido a estos problemas de empaquetamiento he decidido marcar a los ficheros que he subido a RubyForge con la versión 0.1beta1. Os invito a todos los que estais siguiendo esta serie a descargarlos y probarlos:

SprintTracker v0.1

Syndicate content