jueves, 5 de abril de 2012

Un Vistazo Rápido a Arquillian

A través de la cuenta de twitter de Dan Allen @mojavelinux, me he enterado la excelente noticia de que esta excelente Guía de Introducción de Arquillian está disponible a partir de ahora en español. Por ahí se dice que no se puede empezar a escribir pruebas unitarias con Arquillian sin haber leído antes este tutorial. Y es cierto. Así que ya no tenía excusa para probar el flamante framework de JBoss para pruebas de componentes de contenedores.


Como muy bien explica Pablo Felitti en su blog Enterprise Development Ideas, una de las mayores desventajas de usar componentes que se despliegan en contenedores (como EJB, por ejemplo) es que necesitamos levantar el contenedor mismo para probar, ya que el contenedor maneja el ciclo de vida de sus componentes. Levantar un contenedor de EJB, por ejemplo, nos puede consumir mucho tiempo. Esto hace que las pruebas unitarias resulten tediosas para el programador. Muchas veces esto se soluciona de mala manera clasificando las pruebas unitarias que usan EJBs como pruebas de integración y ejecutándolas en una etapa posterior en el ciclo de vida del desarrollo.

Arquillian puede ser una solución para esto. Arquillian nos permite olvidarnos de los mocks, olvidarnos de las chanchadas que hacemos para probar un componente de forma standalone, fuera de su contenedor, y ejecutar pruebas en el ambiente real. Con Arquillian podemos probar un EJB tal cual es, con sus inyecciones de servicios, con su ciclo de vida, en el mismo contenedor, inyectando el componente en el mismo Unit Test, ya que el Unit Test mismo se ejecutará dentro del contenedor.

Seguí el tutorial paso a paso y todo me funcionó de maravilla. El mismo te enseña a crear un proyecto JavaEE 6 de cero usando:
  • Maven (yo en particular usé la versión 3.0.3, que es la que tenía)
  • JavaSE 6
  • JavaEE 6
  • JUnit 4
  • CDI como modelo de programación (contenedor de contexto e inyección de dependencia)
  • Eclipse (opcional; dado que todo el tutorial se puede seguir con otro IDE y usando Maven si se tiene experiencia)
El tutorial comienza de cero, así que no se preocupen si no tienen estas cosas. Lo único que necesitan tener instalado es Apache Maven y conexión a Internet. Hay una parte de la guía en que configura el proyecto Maven usando una herramienta de configuración de proyectos llamada JBoss Forge. Por mi parte tomé el camino más estándar y manual y usé Maven directamente.

A la hora de ejecutar la prueba unitaria, utiliza contenedores embebidos. Esto es una excelente opción para ejecutar rápidamente el test, aunque es mentira que estamos probando en el ambiente real: el ambiente real es el contenedor mismo. La versión embebida del contenedor puede tener distintos bugs o funcionar de forma diferente en algunos casos. De todas formas, repito, es una opción excelente para empezar y probar rápido. Arquillian también permite configurar otros contenedores no embebidos sin cambiar una sola línea de código. La única diferencia es que hay que tenerlos levantados para poder ejecutar los tests.

Los contenedores embebidos con los que la guía prueba son:
  • Weld, la implementación de referencia de CDI
  • JBoss 7 (managed)
  • Glassfish 3 (embedded)
Los tres se configuran en el pom.xml del proyecto mediante perfiles y Maven solito se encarga de descargarlos y Arquillian de activarlos al ejecutar las pruebas, a través de un adaptador para cada contenedor. Arquillian selecciona el contenedor basándose en el adaptador que está disponible en el classpath. Una prueba con Arquillian debe ser ejecutada en cualquier contenedor que es compatible con el modelo de programación utilizado en la prueba (siempre y cuando exista un adaptador de Arquillian para dicho contenedor). Esto significa que el ejemplo del tutorial sólo puede ser corrido en un contenedor CDI como JBoss 6 (o superior), Weld o Glassfish 3 (o superior).

La buena noticia es que en solo diez minutos, siguiendo los pasos, la prueba me ha salido funcionando para los contenedores embebidos. Aquí copio cómo me ha quedado el pom.xml final:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>org.arquillian.example</groupId>
 <artifactId>arquilian-tutorial</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>arquilian-tutorial</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
     <source>1.6</source>
     <target>1.6</target>
    </configuration>
   </plugin>
  </plugins>
 </build>

 <dependencyManagement>
  <dependencies>
   <dependency>
    <groupId>org.jboss.arquillian</groupId>
    <artifactId>arquillian-bom</artifactId>
    <version>1.0.0.CR7</version>
    <scope>import</scope>
    <type>pom</type>
   </dependency>
  </dependencies>
 </dependencyManagement>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.8.1</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>org.jboss.arquillian.junit</groupId>
   <artifactId>arquillian-junit-container</artifactId>
   <scope>test</scope>
  </dependency>
 </dependencies>

 <profiles>
  <profile>
   <id>arquillian-weld-ee-embedded</id>
   <activation>
    <activeByDefault>true</activeByDefault>
   </activation>
   <dependencies>
    <dependency>
     <groupId>org.jboss.spec</groupId>
     <artifactId>jboss-javaee-6.0</artifactId>
     <version>1.0.0.Final</version>
     <type>pom</type>
     <scope>provided</scope>
    </dependency>
    <dependency>
     <groupId>org.jboss.arquillian.container</groupId>
     <artifactId>arquillian-weld-ee-embedded-1.1</artifactId>
     <version>1.0.0.CR2</version>
     <scope>test</scope>
    </dependency>
    <dependency>
     <groupId>org.jboss.weld</groupId>
     <artifactId>weld-core</artifactId>
     <version>1.1.5.Final</version>
     <scope>test</scope>
    </dependency>
    <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-simple</artifactId>
     <version>1.6.4</version>
     <scope>test</scope>
    </dependency>
   </dependencies>
  </profile>

  <profile>
   <id>arquillian-glassfish-embedded</id>
   <dependencies>
    <dependency>
     <groupId>org.jboss.arquillian.container</groupId>
     <artifactId>arquillian-glassfish-embedded-3.1</artifactId>
     <version>1.0.0.CR2</version>
     <scope>test</scope>
    </dependency>
    <dependency>
     <groupId>org.glassfish.main.extras</groupId>
     <artifactId>glassfish-embedded-all</artifactId>
     <version>3.1.2</version>
     <scope>provided</scope>
    </dependency>
   </dependencies>
  </profile>

  <profile>
   <id>arquillian-jbossas-managed</id>
   <dependencies>
    <dependency>
     <groupId>org.jboss.spec</groupId>
     <artifactId>jboss-javaee-6.0</artifactId>
     <version>1.0.0.Final</version>
     <type>pom</type>
     <scope>provided</scope>
    </dependency>
    <dependency>
     <groupId>org.jboss.as</groupId>
     <artifactId>jboss-as-arquillian-container-managed</artifactId>
     <version>7.0.2.Final</version>
     <scope>test</scope>
    </dependency>
   </dependencies>
  </profile>

 </profiles>

</project>

Si usáramos EJB 3.0, Arquillian podría usarse con un JBoss 5.1 o superior. La configuración es un poco más trabajosa, pero puede funcionar. Si usamos EJB 3.1, con un JBoss 6 o JBoss 7 la configuración es más sencilla.

Otra opción a Arquillian es la que propone Pablo Felitti en este post, donde usa un contenedor embebido de EJB y en la prueba unitaria utiliza la interfaz remota del EJB. En el post, Pablo usa Apache OpenEJB, pero podría usar JBoss Embedded o cualquier otro. Ésta es una excelente opción para cuando se está trabajando con EJB 3.0 (JavaEE 5). Las ventajas que brinda Arquillian frente a este enfoque son:
  • No tener que declarar una interfaz remota para el EJB (si no es necesario)
  • Poder inyectar el EJB en el test de la misma forma en que se haría desde algún otro componente dentro del mismo contenedor (usando la interfaz local)
  • Correr el test dentro del contenedor (en el mismo ambiente) y no en otra máquina virtual standalone remota
Para cerrar y ponerle un poco de humor al post, les comparto este gracioso video, compartido originalmente por Dan Allen en Vimeo, donde Hitler se pone loco porque Arquillian deja al descubierto los bugs de su contenedor.