Tutorial: Testes reais de componentes Java EE com Arquillian e ShrinkWrap

Autor: Paulo Jerônimo

Última atualização: 10/07/2010 às 09:32
Comentários: http://blog.ladoservidor.com/2010/07/tutorial-testes-reais-de-componentes.html

Introdução

Testes unitários e mock objects só podem nos levar até determinado ponto. Eventualmente, desejamos ver como nossos componentes se comportam num ambiente real de execução, através dos chamados testes de integração. Visando facilitar a codificação de tais tipos de testes em servidores de aplicações Java EE, a comunidade JBoss, durante o evento Devoxx 2009, apresentou um projeto muito interessante denominado Arquillian.

O Arquillian é uma extensão para o TestNG e para o JUnit que nos permite validar o comportamento de EJBs gerenciáveis por um contêiner ou de POJOs que fazem uso de serviços providos por este, de maneira tão fácil quanto a escrita de um teste unitário. Podemos comparar o Arquillian a uma versão moderna do Cargo, embora seu potencial se estenda muito além.

Utilizando uma API fluente provida pelo ShrinkWrap, grupos de testes são criados como pacotes Java EE, dando ao desenvolvedor um correto ajuste e controle sobre quais recursos precisam ser testados. Os pacotes de testes são então implantados individualmente e executados dentro de um contêiner remoto ou embutido, ou num ambiente CDI bootstrapped. Uma comunicação no estilo RPC (ou local, se aplicável) entre o executor do teste e o ambiente de sua execução negocia que testes serão executados e reporta resultados de volta. Isto significa duas coisas para o desenvolvedor: a primeira é que ele desenvove testes Arquillian como se fossem testes unitários; a segunda é que o ambiente de execução pode ser facilmente trocado.

O desenvolvimento de testes para objetos de negócio reais não deve gerar pausas. Desenvolvedores deveriam estar habilitados a criar testes de integração de maneira tão simples e rápida quanto a criação de uma classe "Calculadora". É neste ponto em que entram o Arquillian e o ShrinkWrap.

Caraterísticas do Arquillian

Com o uso de POJOs (no advento do JEE 5), os desenvolvedores têm facilidades para testar seus componentes fora de um contêiner. Mas, vários dos serviços de infraestrutura oferecidos por este (segurança, transações, etc), desacoplados do negócio através de uma programação declarativa (com o uso de anotações, XML), só podem ser validados por um teste de integração. O Arquillian ajuda a confirmar se a lógica de negócio esta apropriadamente inserida no contexto e valida se os serviços utilizados por ela estão aplicados de forma correta.

O Arquillian se atrela ao ciclo de vida do framework executor de testes utilizado: JUnit (4 ou superior), TestNG (5 ou superior). Então, ele inicia uma reação aos eventos do teste para gerenciar o contêiner (inicialização e parada). Com o auxílio do ShrinkWrap ele também gera um arquivo com as classes e recursos utilizados para a implantação no contêiner, que por sua vez pode ser remoto ou embutido. Um contêiner remoto reside em uma JVM separada da que roda o executor de testes. Já um contêiner embutido reside na mesma JVM do Arquillian. Quando integrado a um contêiner remoto, o Archillian pode gerenciar seu ciclo de vida ou pode simplesmente ligar-se a ele, caso já esteja em execução. No caso de contêineres locais, a maioria das ações é gerenciada pelo Arquillian.

Os contêineres utilizados pelo Arquillian são classificados por suas capacidades e podem ser: servidores Java EE completamente compatíveis com a especificação (ex.: GlassFish, JBoss AS), contêineres embutidos (ex.: Embedded GlassFish, Embedded JBoss, OpenEJB), um contêiner Servlet (ex.: Tomcat, Jetty) ou um contêiner para beans seguindo a especificação CDI (ex.: Weld, Spring). O Arquillian assegura-se de que o contêiner utilizado para testes é plugável. Ele não envolve o uso de APIs que amarram o desenvolvedor a um ambiente de testes proprietário e, desta forma, garante facilidades para a troca do ambiente de testes a qualquer momento.

O Arquillian aprimora as classes de testes com o uso de serviços como injeção, contextos e interceptadores. Estes serviços dão um acesso fácil aos componentes e aos recursos utilizados por eles. Como exemplo, o Arquillian provê injeções, para campos e argumentos de métodos em contêineres Java EE: @Inject, @Resource, @PersistenceUnit, @PersistenceContext, @EJB. Além disto, esta lista pode ser ampliada através do uso de sua SPI (service provider interface).

Baixando, compilando e instalando o Arquillian no repositório local do Maven

Este tópico cobre a instalação do Arquillian num ambiente Linux Ubuntu 10.04 (Lucid), exemplificando os comandos para isto. A vm-curso-ladoservidor pode ser utilizada como um ambiente apropriado para a execução de todos os comandos deste tutorial. Entretanto, o Arquillian é simplesmente uma aplicação Java, independente de sistema operacional. Desta forma, também é possível executar comandos equivalentes para a instalação em diferentes distribuições Linux ou sistemas operacionais.

O Arquillian é um projeto que ainda não tem um release liberado para a instalação de seus binários. Para utilizá-lo, é necessário ter um JDK, o Maven e o Subversion instalados. No Ubuntu 10.04, nossa escolha de JDK aponta para a instalação do OpenJDK mas existem outras alternativas, como explicado no tópico "Instalando um Java Development Kit (JDK)" do tutorial "Instalando, iniciando, testando e parando o JBoss AS 6".

A listagem 01 apresenta passos para a instalação do OpenJDK, para a criação de um novo usuário (nomeado arquillian) e para a instalação do Maven abaixo abaixo do $HOME deste usuário. Não é necessária a criação do usuário arquillian. Entretanto, isto é realizado apenas para não ocorrer a configuração de arquivos do usuário corrente com informações para um ambiente de testes do Arquillian. Para uma introdução melhor a respeito do Maven, leia o tutorial "Utilizando Maven, Nexus e Subversion em projetos Java (Parte I)".

Listagem 01. Instalando o OpenJDK, criando o usuário "arquillian" e instalando o Maven $ sudo apt-get install openjdk-6-jdk
$ sudo useradd -m -s /bin/bash arquillian
$ sudo su - arquillian
$ cat > instalar-maven <<'FIM'
#!/bin/bash
LOG=/tmp/$$.log
INSTALADORES_DIR=~/instaladores
FERRAMENTAS_DIR=~/ferramentas
MAVEN_INSTALADOR=apache-maven-3.0-beta-1-bin.tar.gz
MAVEN_DIR=apache-maven-3.0-beta-1
MAVEN_LINK=maven
MAVEN_URL_INSTALADOR="http://linorg.usp.br/apache/maven/binaries/apache-maven-3.0-beta-1-bin.tar.gz"
ok_ou_falha() { [ $? = 0 ] && echo Ok || { echo "Falhou! Veja $LOG"; exit 1; }; }
mkdir -p $INSTALADORES_DIR && cd $INSTALADORES_DIR
[ -f "$MAVEN_INSTALADOR" ] || {
  echo -n "Baixando $MAVEN_INSTALADOR... "
  wget "$MAVEN_URL_INSTALADOR" &> $LOG
  ok_ou_falha
}
mkdir -p $FERRAMENTAS_DIR && cd $FERRAMENTAS_DIR
echo -n "Extraindo $MAVEN_INSTALADOR... "
rm -rf $MAVEN_DIR; tar xvfz $INSTALADORES_DIR/$MAVEN_INSTALADOR &> $LOG
ok_ou_falha
echo -n "Criando o link $MAVEN_LINK para $MAVEN_DIR... "
rm -f $MAVEN_LINK; ln -s $MAVEN_DIR $MAVEN_LINK
ok_ou_falha
grep '^export JAVA_HOME' ~/.bashrc &> /dev/null || {
  echo -n 'Ajustando JAVA_HOME... '
  echo 'export JAVA_HOME=/usr/lib/jvm/java-6-openjdk' >> ~/.bashrc
  ok_ou_falha
}
grep '^export M2_HOME' ~/.bashrc &> /dev/null && exit 0
echo -n "Ajustando variáveis de ambiente para o funcionando do Maven... "
cat >> ~/.bashrc <<'EOF'
export M2_HOME=FERRAMENTAS_DIR/MAVEN_LINK
export MAVEN_OPTS='-Xmx512m -Djava.awt.headless=true'
export PATH=$M2_HOME/bin:$PATH
EOF
sed -i "
s,FERRAMENTAS_DIR,$FERRAMENTAS_DIR,g
s,MAVEN_LINK,$MAVEN_LINK,g
" ~/.bashrc
ok_ou_falha
FIM
$ chmod +x instalar-maven && ./instalar-maven
$ source ~/.bashrc
$ mkdir -p ~/.m2

Também observando a listagem 01, podemos notar que o script instalar-maven é criado e executado. Este script poderá ser utilizado para fazer (e refazer) a instalação do Maven, a qualquer momento em que isto for necessário. Após a execução do script, o Maven estará instalado no diretório ~/ferramentas/maven. Foge ao escopo deste tutorial a explicação detalhada do funcionamento do script. Entretanto, seu mecanismo de trabalho é praticamente idêntico ao do script instalar-jboss (que será executado mais a frente) e cujas tarefas são explicados minuciosamente no tópico "Baixando e Instalando o JBoss".

Com relação a instalação do Maven temos apenas dois detalhes a acrescentar, relativos a sua configuração para a compilação do Arquillian:

Após a instalação do Maven, os dois últimos comandos da listagem 01 realizam:

Antes da execução dos testes com o Arquillian, o Maven precisará estar configurado para que ele possa encontrar seus jars no repositório do JBoss. Isto ocorre em função do grupo JBoss ter passado a utilizar um novo repositório para os jars de seus projetos, separando-os do repositório central. A forma mais simples de se adicionar este novo repositório ao Maven é ter o arquivo ~/.m2/settings.xml configurado conforme a listagem 02. O conteúdo deste arquivo também pode ser baixado do wiki do JBoss, na página http://community.jboss.org/wiki/MavenSettingsExample-Users.

Listagem 02. Configurando o Maven para acesso aos repositórios do JBoss $ cat > ~/.m2/settings.xml <<FIM
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <pluginGroups>
    <pluginGroup>org.jboss.maven.plugins</pluginGroup>
  </pluginGroups>
  <profiles>
    <profile>
      <id>jboss-public-repository</id>
      <repositories>
        <repository>
          <id>jboss-public-repository-group</id>
          <name>JBoss Public Maven Repository Group</name>
          <url>https://repository.jboss.org/nexus/content/groups/public/</url>
          <layout>default</layout>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>jboss-public-repository-group</id>
          <name>JBoss Public Maven Repository Group</name>
          <url>https://repository.jboss.org/nexus/content/groups/public/</url>
          <layout>default</layout>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>jboss-public-repository</activeProfile>
  </activeProfiles>
</settings>
FIM

Após o arquivo settings.xml ter sido configurado com a inclusão dos novos repositórios (necessários para o build dos fontes em trunk), o próximo passo é baixar o Arquillian, que vem com uma série de exemplos para teste. Isto pode ser realizado pelo uso do Subversion, conforme exemplificado na listagem 03. Iremos utilizar os fontes da tag 1.0.0.Alpha2. Os fontes em trunk estão em constante desenvolvimento e sujeitos a mudanças. Podemos tentar baixar e compilar trunk com os mesmos passos descritos aqui, apenas ajustando a o comando svn co p/ a URL do trunk e o link simbólico criado em seguida. Entretanto, para evitar quaisquer surpresas na construção do projeto, e seguir o passo a passo apresentado neste tutorial, a melhor opção é baixar os fontes sob a tag informada.

Listagem 03. Download dos fontes do Arquillian $ bash -c '
cd
svn co http://anonsvn.jboss.org/repos/common/arquillian/tags/1.0.0.Alpha2/ arquillian-1.0.0.Alpha2
ln -s arquillian-1.0.0.Alpha2 arquillian
'

Antes de partir para a compilação dos exemplos do Arquillian, é interessante executar o comando mvn install em seu diretório base. Isto poderá demorar algum tempo já que o Maven fará o download de todas as dependências não encontradas para o projeto. Entretanto, ao final deste processo, as várias dependências necessárias para a utilização do Arquillian serão instaladas no repositório local do usuário (~./m2). A listagem 04 apresenta os comandos deste processo.

Listagem 04. Compilação e instalação dos jars do Arquillian no repositório local $ bash -c '
cd ~/arquillian
mvn install
'

Instalando e colocando o JBoss AS 6 em execução

Para executar alguns dos exemplos de testes que vem com o Arquillian, deveremos ter um JBoss AS instalado. A instalação deste servidor pode ser realizada conforme detalhado no tópico "Baixando e Instalando o JBoss", do tutorial "Instalando, iniciando, testando e parando o JBoss AS 6". Neste tópico o script instalar-jboss, que é replicado e executado pelos comandos da listagem 05, é completamente explicado.

Listagem 05. Instalação do JBoss AS $ cat > instalar-jboss <<'FIM'
#!/bin/bash
LOG=/tmp/$$.log
INSTALADORES_DIR=~/instaladores
FERRAMENTAS_DIR=~/ferramentas
JBOSS_INSTALADOR=jboss-as-distribution-6.0.0.20100429-M3.zip
JBOSS_DIR=jboss-6.0.0.20100429-M3
JBOSS_LINK=jboss
JBOSS_URL_INSTALADOR="http://downloads.sourceforge.net/project/jboss/JBoss/JBoss-6.0.0.M3/ARQUIVO?use_mirror=ufpr"
ok_ou_falha() { [ $? = 0 ] && echo Ok || { echo "Falhou! Veja $LOG"; exit 1; }; }
mkdir -p $INSTALADORES_DIR && cd $INSTALADORES_DIR
[ -f "$JBOSS_INSTALADOR" -a -f "$JBOSS_INSTALADOR.md5" ] || {
  for f in $JBOSS_INSTALADOR $JBOSS_INSTALADOR.md5; do
    [ -f "$f" ] && continue
    echo -n "Baixando $f... "
    wget "`echo $JBOSS_URL_INSTALADOR | sed s,ARQUIVO,$f,g`" &> $LOG
    ok_ou_falha
  done
}
echo -n "Verificando o checksum do arquivo $JBOSS_INSTALADOR... "
test `md5sum $JBOSS_INSTALADOR | cut -d ' ' -f 1` = `cat $JBOSS_INSTALADOR.md5`
ok_ou_falha
mkdir -p $FERRAMENTAS_DIR && cd $FERRAMENTAS_DIR
echo -n "Extraindo $JBOSS_INSTALADOR... "
rm -rf $JBOSS_DIR; unzip $INSTALADORES_DIR/$JBOSS_INSTALADOR &> $LOG
ok_ou_falha
echo -n "Criando o link $JBOSS_LINK para $JBOSS_DIR... "
rm -f $JBOSS_LINK; ln -s $JBOSS_DIR $JBOSS_LINK
ok_ou_falha
grep '^export JAVA_HOME' ~/.bashrc &> /dev/null || {
  echo -n 'Ajustando JAVA_HOME... '
  echo 'export JAVA_HOME=/usr/lib/jvm/java-6-openjdk' >> ~/.bashrc
  ok_ou_falha
}
grep '^export JBOSS_HOME' ~/.bashrc &> /dev/null && exit 0
echo -n "Ajustando variáveis de ambiente para o funcionando do JBoss... "
cat >> ~/.bashrc <<'EOF'
export JBOSS_HOME=FERRAMENTAS_DIR/JBOSS_LINK
export PATH=$JBOSS_HOME/bin:$PATH
EOF
sed -i "
s,FERRAMENTAS_DIR,$FERRAMENTAS_DIR,g
s,JBOSS_LINK,$JBOSS_LINK,g
" ~/.bashrc
ok_ou_falha
FIM
$ chmod +x instalar-jboss && ./instalar-jboss
$ source ~/.bashrc

A execução dos comandos apresentados na listagem 06, gera o arquivo /tmp/jboss.log que armazenará as saídas padrão e de erros, inicializará o servidor de forma independente do shell corrente, em background e, por fim, apresentará a saída que for sendo gerada (através do tail -f).

Listagem 06. Execução do JBoss AS $ JBOSS_CONSOLE=/tmp/jboss.log; nohup run.sh &> $JBOSS_CONSOLE & tail -f $JBOSS_CONSOLE

Executando testes com Arquillian/JUnit

Ao executarmos os comandos da listagem 07, o Maven conduzirá o Arquillian a fase de testes para que ele se ligue ao JBoss AS e realize os testes da aplicação exemplo utilizando o JUnit. Para que haja visibilidade do log de execução do JBoss no shell corrente, é interessante que os comandos sejam executados em um novo shell. Desta forma, também será possível visualizar o resultado da execução dos testes, que gera como saída, o resultado apresentado na listagem 08.

O parâmetro "-Pjbossas-remote-60" passado ao maven, diz a ele qual será o profile utilizado para a execução dos testes. Neste caso, o Arquillian irá se conectar a um JBoss AS 6, cuja execução está ocorrendo numa JVM diferente da que irá rodar os testes (ou seja, remoto). Outros valores para este parâmetro podem ser informados. Por exemplo, se desejássemos testar a aplicação em um GlassFish v3 embutido, o valor informado seria "-Pglassfish-embedded-30".

Listagem 07. Teste de exemplo do Arquillian no JUnit $ bash -c '
cd ~/arquillian/examples/junit
mvn test -Pjbossas-remote-60
'

Listagem 08. Saída do teste de exemplo do Arquillian no JUnit  1 [INFO] Scanning for projects...
 2 [WARNING]
 3 [WARNING] Some problems were encountered while building the effective model for org.jboss.arquillian.example:arquillian-example-junit:jar:1.0.0.Alpha2
 4 [WARNING] 'parent.relativePath' of POM org.jboss.arquillian:arquillian-build:1.0.0.Alpha2 (/home/arquillian/ferramentas/arquillian-1.0.0.Alpha2/build/pom.xml) points at org.jboss.arquillian:arquillian-parent instead of org.jboss:jboss-parent, please verify your project structure @ org.jboss.arquillian:arquillian-build:1.0.0.Alpha2, /home/arquillian/ferramentas/arquillian-1.0.0.Alpha2/build/pom.xml
 5 [WARNING] 'reporting.plugins.plugin.version' for org.codehaus.mojo:cobertura-maven-plugin is missing. @
 6 [WARNING] 'reporting.plugins.plugin.version' for org.codehaus.mojo:findbugs-maven-plugin is missing. @
 7 [WARNING]
 8 [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
 9 [WARNING]
10 [WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
11 [WARNING]
12 [INFO]                                                                        
13 [INFO] ------------------------------------------------------------------------
14 [INFO] Building Arquillian Example JUnit 1.0.0.Alpha2
15 [INFO] ------------------------------------------------------------------------
16 [INFO]
17 [INFO] --- maven-enforcer-plugin:1.0-beta-1:enforce (enforce-plugin-versions) @ arquillian-example-junit ---
18 [WARNING] This rule is not compatible with the current version of Maven. The rule is not able to perform any checks.
19 [INFO]
20 [INFO] --- maven-enforcer-plugin:1.0-beta-1:enforce (enforce-java-version) @ arquillian-example-junit ---
21 [INFO]
22 [INFO] --- maven-enforcer-plugin:1.0-beta-1:enforce (enforce-maven-version) @ arquillian-example-junit ---
23 [INFO]
24 [INFO] --- maven-enforcer-plugin:1.0-beta-1:enforce (enforce-maven-environment) @ arquillian-example-junit ---
25 [INFO]
26 [INFO] --- maven-resources-plugin:2.4.1:resources (default-resources) @ arquillian-example-junit ---
27 [INFO] Using 'UTF-8' encoding to copy filtered resources.
28 [INFO] Copying 0 resource
29 [INFO]
30 [INFO] --- maven-compiler-plugin:2.0.2:compile (default-compile) @ arquillian-example-junit ---
31 [INFO] Nothing to compile - all classes are up to date
32 [INFO]
33 [INFO] --- maven-resources-plugin:2.4.1:testResources (default-testResources) @ arquillian-example-junit ---
34 [INFO] Using 'UTF-8' encoding to copy filtered resources.
35 [INFO] Copying 1 resource
36 [INFO]
37 [INFO] --- maven-compiler-plugin:2.0.2:testCompile (default-testCompile) @ arquillian-example-junit ---
38 [INFO] Nothing to compile - all classes are up to date
39 [INFO]
40 [INFO] --- maven-surefire-plugin:2.4.3:test (default-test) @ arquillian-example-junit ---
41 [INFO] Surefire report directory: /home/arquillian/ferramentas/arquillian-1.0.0.Alpha2/examples/junit/target/surefire-reports
42
43 -------------------------------------------------------
44  T E S T S
45 -------------------------------------------------------
46 Running com.acme.ejb.TemperatureConverterTestCase
47 Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.474 sec
48 Running com.acme.resource.InjectionTestCase
49 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.286 sec
50 Running com.acme.jms.InjectionTestCase
51 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.668 sec
52 Running com.acme.ejb31.NoInterfaceEJBTestCase
53 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.77 sec
54 Running com.acme.cdi.random.RandomTestCase
55 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.875 sec
56 Running com.acme.web.LocalRunServletTestCase
57 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.356 sec
58 Running com.acme.cdi.payment.SynchronousPaymentProcessorTestCase
59 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.336 sec
60 Running com.acme.ejb.InjectionTestCase
61 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.299 sec
62
63 Results :
64
65 Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
66
67 [INFO] ------------------------------------------------------------------------
68 [INFO] BUILD SUCCESS
69 [INFO] ------------------------------------------------------------------------
70 [INFO] Total time: 44.841s
71 [INFO] Finished at: Fri Jul 09 07:32:10 BRT 2010
72 [INFO] Final Memory: 11M/22M
73 [INFO] ------------------------------------------------------------------------

A listagem 09 apresenta um trecho do log do JBoss AS durante a execução dos testes. Observe pelo log que o Arquillian cuida, com o auxílio do ShrinkWrap, do processo de empacotamento de um arquivo implantado com o nome test.ear (linha 1). Note também, que o Arquillian realiza o processo inverso (a desimplantação do pacote), ao final do teste (linha 32). A cada TestCase executado, este processo é repetido: isto pode ser acompanhado pela atenta observação do log.

Listagem 09. Linhas de log (parciais e editadas) do JBoss AS, durante a execução dos testes   1 07:31:43,901 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Handle stream, deploymentTarget: names=[test.war], description=http://localhost:9999/test.war
  2 07:31:44,100 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End handle stream, repositoryName: vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war
  3 07:31:44,132 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Begin start, [test.war]
  4 07:31:46,276 INFO  [org.jboss.ejb3.deployers.Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@28050534{vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war}
  5 07:31:46,277 INFO  [org.jboss.ejb3.deployers.Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@28050534{vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war}
  6 07:31:46,293 WARN  [org.jboss.ejb3.interceptor.InterceptorInfoRepository] EJBTHREE-1852: InterceptorInfoRepository is deprecated
  7 07:31:46,870 INFO  [org.jboss.ejb3.deployers.JBossASKernel] Created KernelDeployment for: test.war
  8 07:31:46,874 INFO  [org.jboss.ejb3.deployers.JBossASKernel] installing bean: jboss.j2ee:jar=test.war,name=TemperatureConverterBean,service=EJB3
  9 07:31:46,875 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   with dependencies:
 10 07:31:46,875 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   and demands:
 11 07:31:46,875 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jboss.ejb:service=EJBTimerService; Required: Described
 12 07:31:46,875 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   and supplies:
 13 07:31:46,876 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jndi:TemperatureConverterBean/local-com.acme.ejb.TemperatureConverter
 14 07:31:46,876 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jndi:TemperatureConverterBean/local
 15 07:31:46,876 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jndi:TemperatureConverterBean
 16 07:31:46,876 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     Class:com.acme.ejb.TemperatureConverter
 17 07:31:46,879 INFO  [org.jboss.ejb3.deployers.JBossASKernel] Added bean(jboss.j2ee:jar=test.war,name=TemperatureConverterBean,service=EJB3) to KernelDeployment of: test.war
 18 07:31:47,179 INFO  [org.jboss.ejb3.session.SessionSpecContainer] Starting jboss.j2ee:jar=test.war,name=TemperatureConverterBean,service=EJB3
 19 07:31:47,190 INFO  [org.jboss.ejb3.EJBContainer] STARTED EJB: com.acme.ejb.TemperatureConverterBean ejbName: TemperatureConverterBean
 20 07:31:47,228 INFO  [org.jboss.ejb3.proxy.impl.jndiregistrar.JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:
 21
 22         TemperatureConverterBean/local - EJB3.x Default Local Business Interface
 23         TemperatureConverterBean/local-com.acme.ejb.TemperatureConverter - EJB3.x Local Business Interface
 24
 25 07:31:47,290 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] deploy, ctxPath=/test
 26 07:31:47,395 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End start, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
 27 07:31:48,098 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Stop, [test.war]
 28 07:31:48,101 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] undeploy, ctxPath=/test
 29 07:31:48,117 INFO  [org.jboss.ejb3.session.SessionSpecContainer] Stopping jboss.j2ee:jar=test.war,name=TemperatureConverterBean,service=EJB3
 30 07:31:48,130 INFO  [org.jboss.ejb3.EJBContainer] STOPPED EJB: com.acme.ejb.TemperatureConverterBean ejbName: TemperatureConverterBean
 31 07:31:48,189 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End stop, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
 32 07:31:48,224 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Remove, [test.war]
 33 07:31:48,868 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Handle stream, deploymentTarget: names=[test.war], description=http://localhost:9999/test.war
 34 07:31:49,058 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End handle stream, repositoryName: vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war
 35 07:31:49,089 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Begin start, [test.war]
 36 07:31:51,262 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] deploy, ctxPath=/test
 37 07:31:51,332 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End start, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
 38 07:31:51,442 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Stop, [test.war]
 39 07:31:51,443 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] undeploy, ctxPath=/test
 40 07:31:51,490 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End stop, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
 41 07:31:51,516 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Remove, [test.war]
 42 07:31:52,061 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Handle stream, deploymentTarget: names=[test.war], description=http://localhost:9999/test.war
 43 07:31:52,261 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End handle stream, repositoryName: vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war
 44 07:31:52,285 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Begin start, [test.war]
 45 07:31:53,643 INFO  [org.jboss.deployment.dependency.ContainerDependencyMetaData] addJndiDependency, JndiDependencyMetaData@e7630{/queue/DLQ}
 46 07:31:53,656 INFO  [org.jboss.ejb3.deployers.Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@2990202{vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war}
 47 07:31:53,657 INFO  [org.jboss.ejb3.deployers.Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@2990202{vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war}
 48 07:31:53,658 WARN  [org.jboss.ejb3.interceptor.InterceptorInfoRepository] EJBTHREE-1852: InterceptorInfoRepository is deprecated
 49 07:31:53,730 INFO  [org.jboss.ejb3.deployers.JBossASKernel] Created KernelDeployment for: test.war
 50 07:31:53,731 INFO  [org.jboss.ejb3.deployers.JBossASKernel] installing bean: jboss.j2ee:jar=test.war,name=MessageEcho,service=EJB3
 51 07:31:53,731 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   with dependencies:
 52 07:31:53,731 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   and demands:
 53 07:31:53,731 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jboss.ejb:service=EJBTimerService; Required: Described
 54 07:31:53,731 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   and supplies:
 55 07:31:53,732 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jndi:null
 56 07:31:53,732 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     Class:javax.jms.MessageListener
 57 07:31:53,732 INFO  [org.jboss.ejb3.deployers.JBossASKernel] Added bean(jboss.j2ee:jar=test.war,name=MessageEcho,service=EJB3) to KernelDeployment of: test.war
 58 07:31:54,006 INFO  [org.jboss.ejb3.EJBContainer] STARTED EJB: com.acme.ejb.MessageEcho ejbName: MessageEcho
 59 07:31:54,146 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] deploy, ctxPath=/test
 60 07:31:54,282 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End start, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
 61 07:31:54,864 INFO  [STDOUT] received ID:31ba6ec2-8b45-11df-966b-0018de97c70e
 62 07:31:55,010 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Stop, [test.war]
 63 07:31:55,012 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] undeploy, ctxPath=/test
 64 07:31:55,088 INFO  [org.jboss.ejb3.EJBContainer] STOPPED EJB: com.acme.ejb.MessageEcho ejbName: MessageEcho
 65 07:31:55,155 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End stop, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
 66 07:31:55,189 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Remove, [test.war]
 67 07:31:55,851 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Handle stream, deploymentTarget: names=[test.war], description=http://localhost:9999/test.war
 68 07:31:55,987 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End handle stream, repositoryName: vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war
 69 07:31:56,013 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Begin start, [test.war]
 70 07:31:57,330 INFO  [org.jboss.ejb3.deployers.Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@33276498{vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war}
 71 07:31:57,330 INFO  [org.jboss.ejb3.deployers.Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@33276498{vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war}
 72 07:31:57,331 WARN  [org.jboss.ejb3.interceptor.InterceptorInfoRepository] EJBTHREE-1852: InterceptorInfoRepository is deprecated
 73 07:31:57,363 INFO  [org.jboss.ejb3.deployers.JBossASKernel] Created KernelDeployment for: test.war
 74 07:31:57,363 INFO  [org.jboss.ejb3.deployers.JBossASKernel] installing bean: jboss.j2ee:jar=test.war,name=NoInterfaceEJB,service=EJB3
 75 07:31:57,363 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   with dependencies:
 76 07:31:57,363 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   and demands:
 77 07:31:57,363 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jboss.ejb:service=EJBTimerService; Required: Described
 78 07:31:57,364 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   and supplies:
 79 07:31:57,364 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jndi:NoInterfaceEJB
 80 07:31:57,364 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jndi:NoInterfaceEJB/no-interface
 81 07:31:57,364 INFO  [org.jboss.ejb3.deployers.JBossASKernel] Added bean(jboss.j2ee:jar=test.war,name=NoInterfaceEJB,service=EJB3) to KernelDeployment of: test.war
 82 07:31:57,644 INFO  [org.jboss.ejb3.nointerface.impl.jndi.AbstractNoInterfaceViewJNDIBinder] Binding the following entry in Global JNDI:
 83
 84         NoInterfaceEJB/no-interface - EJB3.1 no-interface view
 85
 86 07:31:57,649 INFO  [org.jboss.ejb3.session.SessionSpecContainer] Starting jboss.j2ee:jar=test.war,name=NoInterfaceEJB,service=EJB3
 87 07:31:57,650 INFO  [org.jboss.ejb3.EJBContainer] STARTED EJB: com.acme.ejb31.NoInterfaceEJB ejbName: NoInterfaceEJB
 88 07:31:57,650 INFO  [org.jboss.ejb3.proxy.impl.jndiregistrar.JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:
 89
 90
 91 07:31:57,670 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] deploy, ctxPath=/test
 92 07:31:57,723 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End start, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
 93 07:31:57,794 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Stop, [test.war]
 94 07:31:57,796 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] undeploy, ctxPath=/test
 95 07:31:57,812 INFO  [org.jboss.ejb3.session.SessionSpecContainer] Stopping jboss.j2ee:jar=test.war,name=NoInterfaceEJB,service=EJB3
 96 07:31:57,813 INFO  [org.jboss.ejb3.EJBContainer] STOPPED EJB: com.acme.ejb31.NoInterfaceEJB ejbName: NoInterfaceEJB
 97 07:31:57,934 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End stop, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
 98 07:31:57,960 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Remove, [test.war]
 99 07:31:58,595 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Handle stream, deploymentTarget: names=[test.war], description=http://localhost:9999/test.war
100 07:31:58,796 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End handle stream, repositoryName: vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war
101 07:31:58,839 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Begin start, [test.war]
102 07:32:02,056 INFO  [org.jboss.weld.Version] WELD-000900 1.0.1 (CR2)
103 07:32:02,385 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] deploy, ctxPath=/test
104 07:32:02,658 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End start, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
105 07:32:02,745 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Stop, [test.war]
106 07:32:02,747 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] undeploy, ctxPath=/test
107 07:32:02,808 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End stop, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
108 07:32:02,837 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Remove, [test.war]
109 07:32:02,893 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Handle stream, deploymentTarget: names=[test.war], description=http://localhost:9999/test.war
110 07:32:02,907 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End handle stream, repositoryName: vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war
111 07:32:02,931 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Begin start, [test.war]
112 07:32:03,017 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] deploy, ctxPath=/test
113 07:32:03,083 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End start, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
114 07:32:03,138 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Stop, [test.war]
115 07:32:03,139 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] undeploy, ctxPath=/test
116 07:32:03,160 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End stop, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
117 07:32:03,194 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Remove, [test.war]
118 07:32:03,648 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Handle stream, deploymentTarget: names=[test.war], description=http://localhost:9999/test.war
119 07:32:03,794 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End handle stream, repositoryName: vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war
120 07:32:03,820 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Begin start, [test.war]
121 07:32:07,038 INFO  [org.jboss.weld.Version] WELD-000900 1.0.1 (CR2)
122 07:32:07,197 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] deploy, ctxPath=/test
123 07:32:07,331 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End start, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
124 07:32:07,441 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Stop, [test.war]
125 07:32:07,442 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] undeploy, ctxPath=/test
126 07:32:07,505 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End stop, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
127 07:32:07,526 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Remove, [test.war]
128 07:32:08,111 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Handle stream, deploymentTarget: names=[test.war], description=http://localhost:9999/test.war
129 07:32:08,217 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End handle stream, repositoryName: vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war
130 07:32:08,239 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Begin start, [test.war]
131 07:32:09,155 INFO  [org.jboss.ejb3.deployers.Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@23099330{vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war}
132 07:32:09,155 INFO  [org.jboss.ejb3.deployers.Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@23099330{vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war}
133 07:32:09,156 WARN  [org.jboss.ejb3.interceptor.InterceptorInfoRepository] EJBTHREE-1852: InterceptorInfoRepository is deprecated
134 07:32:09,189 INFO  [org.jboss.ejb3.deployers.JBossASKernel] Created KernelDeployment for: test.war
135 07:32:09,189 INFO  [org.jboss.ejb3.deployers.JBossASKernel] installing bean: jboss.j2ee:jar=test.war,name=GreetingManagerBean,service=EJB3
136 07:32:09,189 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   with dependencies:
137 07:32:09,189 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   and demands:
138 07:32:09,189 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jboss.ejb:service=EJBTimerService; Required: Described
139 07:32:09,190 INFO  [org.jboss.ejb3.deployers.JBossASKernel]   and supplies:
140 07:32:09,190 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jndi:GreetingManagerBean
141 07:32:09,190 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jndi:GreetingManagerBean/local
142 07:32:09,190 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     Class:com.acme.ejb.GreetingManager
143 07:32:09,190 INFO  [org.jboss.ejb3.deployers.JBossASKernel]     jndi:GreetingManagerBean/local-com.acme.ejb.GreetingManager
144 07:32:09,191 INFO  [org.jboss.ejb3.deployers.JBossASKernel] Added bean(jboss.j2ee:jar=test.war,name=GreetingManagerBean,service=EJB3) to KernelDeployment of: test.war
145 07:32:09,378 INFO  [org.jboss.ejb3.session.SessionSpecContainer] Starting jboss.j2ee:jar=test.war,name=GreetingManagerBean,service=EJB3
146 07:32:09,379 INFO  [org.jboss.ejb3.EJBContainer] STARTED EJB: com.acme.ejb.GreetingManagerBean ejbName: GreetingManagerBean
147 07:32:09,385 INFO  [org.jboss.ejb3.proxy.impl.jndiregistrar.JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:
148
149         GreetingManagerBean/local - EJB3.x Default Local Business Interface
150         GreetingManagerBean/local-com.acme.ejb.GreetingManager - EJB3.x Local Business Interface
151
152 07:32:09,413 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] deploy, ctxPath=/test
153 07:32:09,510 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End start, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
154 07:32:09,638 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Stop, [test.war]
155 07:32:09,640 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] undeploy, ctxPath=/test
156 07:32:09,659 INFO  [org.jboss.ejb3.session.SessionSpecContainer] Stopping jboss.j2ee:jar=test.war,name=GreetingManagerBean,service=EJB3
157 07:32:09,667 INFO  [org.jboss.ejb3.EJBContainer] STOPPED EJB: com.acme.ejb.GreetingManagerBean ejbName: GreetingManagerBean
158 07:32:09,785 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] End stop, [vfs:///home/arquillian/ferramentas/jboss-6.0.0.20100429-M3/server/default/deploy/test.war]
159 07:32:09,829 INFO  [org.jboss.profileservice.management.upload.remoting.DeployHandler] Remove, [test.war]

Para entender melhor o que está ocorrendo iremos, de agora em diante, analisar este exemplo. Pela listagem 08, linha 46, podemos notar que o primeiro TestCase JUnit a ser executado é o com.acme.ejb.TemperatureConverterTestCase. Logo, é por ele que iniciaremos a exploração do exemplo. Mas antes de visualizarmos este código em seu arquivo correspondente, vamos ver a estrutura de diretórios em que ele está inserido. Os comandos da listagem 10 fazem a mudança para o diretório do exemplo, limpam o código construído pelo Maven e apresentam esta estrutura.

Listagem 10. Arquivos/diretórios em ~/arquillian/examples/junit/ $ cd ~/arquillian/examples/junit
$ mvn clean
$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   └── resources
    └── test
        ├── java
        │   └── com
        │       └── acme
        │           ├── cdi
        │           │   ├── InjectionTestCase.java
        │           │   ├── payment
        │           │   │   └── SynchronousPaymentProcessorTestCase.java
        │           │   └── random
        │           │       └── RandomTestCase.java
        │           ├── ejb
        │           │   ├── InjectionTestCase.java
        │           │   └── TemperatureConverterTestCase.java
        │           ├── ejb31
        │           │   └── NoInterfaceEJBTestCase.java
        │           ├── jms
        │           │   └── InjectionTestCase.java
        │           ├── resource
        │           │   └── InjectionTestCase.java
        │           └── web
        │               └── LocalRunServletTestCase.java
        ├── jboss-resources
        │   └── jndi.properties
        └── resources

18 directories, 11 files

As classes deste projeto utilizam classes empacotadas em outro jar, e que estão disponíveis no diretório ../domain. Estas classes podem ser visualizadas pela execução dos comandos apresentados na listagem a seguir:

Listagem 11. Arquivos/diretórios em ~/arquillian/examples/domain $ cd ../domain
$ mvn clean
$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── acme
    │   │           ├── cdi
    │   │           │   ├── payment
    │   │           │   │   ├── Asynchronous.java
    │   │           │   │   ├── AsynchronousPaymentProcessor.java
    │   │           │   │   ├── MockPaymentProcessor.java
    │   │           │   │   ├── PaymentProcessor.java
    │   │           │   │   ├── Synchronous.java
    │   │           │   │   └── SynchronousPaymentProcessor.java
    │   │           │   ├── random
    │   │           │   │   ├── Random.java
    │   │           │   │   └── RandomNumberGenerator.java
    │   │           │   └── translate
    │   │           │       ├── NorwegianTranslator.java
    │   │           │       ├── SentenceParser.java
    │   │           │       ├── TextTranslator.java
    │   │           │       ├── TranslateController.java
    │   │           │       └── Translator.java
    │   │           ├── ejb
    │   │           │   ├── GreetingManagerBean.java
    │   │           │   ├── GreetingManager.java
    │   │           │   ├── MessageEcho.java
    │   │           │   ├── TemperatureConverterBean.java
    │   │           │   └── TemperatureConverter.java
    │   │           ├── ejb31
    │   │           │   └── NoInterfaceEJB.java
    │   │           ├── jpa
    │   │           │   ├── User.java
    │   │           │   ├── UserRepositoryBean.java
    │   │           │   └── UserRepository.java
    │   │           ├── util
    │   │           │   └── jms
    │   │           │       └── QueueRequestor.java
    │   │           └── web
    │   │               └── TestServlet.java
    │   └── resources
    │       └── com
    │           └── acme
    │               ├── cdi
    │               │   └── payment
    │               │       └── beans.xml
    │               └── jpa
    │                   └── user-test-persistence.xml
    └── test
        ├── java
        └── resources

24 directories, 27 files

Testando EJB

O código da classe com.acme.ejb.TemperatureConverterTestCase está abaixo do diretório src/test/java. Seus trechos fundamentais são apresentados na listagem 12.

Listagem 12. com.acme.ejb.TemperatureConverterTestCase.java  1 @RunWith(org.jboss.arquillian.junit.Arquillian.class)
 2 public class TemperatureConverterTestCase
 3 {
 4
 5    @EJB
 6    private TemperatureConverter converter;
 7
 8    @Deployment
 9    public static JavaArchive createTestArchive() {
10       return ShrinkWrap.create("test.jar", JavaArchive.class)
11          .addClasses(TemperatureConverter.class, TemperatureConverterBean.class);
12    }
13
14    @Test
15    public void testConvertToCelsius() {
16       Assert.assertEquals(converter.convertToCelsius(32d), 0d, 0d);
17       Assert.assertEquals(converter.convertToCelsius(212d), 100d, 0d);
18    }
19
20    @Test
21    public void testConvertToFarenheit() {
22       Assert.assertEquals(converter.convertToFarenheit(0d), 32d, 0d);
23       Assert.assertEquals(converter.convertToFarenheit(100d), 212d, 0d);
24    }
25
26    @Test
27    public void testIsTransactional() {
28       Assert.assertTrue(converter.isTransactional());
29    }
30
31 }

A primeira coisa que chama atenção no código do TestCase apresentado na listagem 12 é o uso da anotação @RunWith, na linha 1. Através de seu uso, o JUnit é informado que o controlador do teste é o Arquillian. Sendo assim, o Arquillian irá procurar por um método estático marcado com a anotação @Deployment que irá criar uma micro implantação para ser realizada no contêiner. Neste TestCase, o método que possui esta anotação é o createTestArquive (linha 9) cujo objetivo é gerar, como o próprio nome diz, um arquivo para a implantação no contêiner. Para realizar a construção do arquivo que será implantado no contêiner, o Arquillian conta com a ajuda do ShrinkWrap.

O papel do ShrinkWrap é o de simplesmente realizar o que está apresentado no trecho de código do método createTestArquive: montar um pacote de classes, através de uma interface fluente em Java, para ser implantado em contêineres. Ele provê um mecanismo para a montagem de JARs, WARs e EARs, utilizando o Java como a linguagem para isto.

O ShrinkWrap não possui dependências externas e opcionalmente, possibilita a exportação do conteúdo dos pacotes no formato ZIP ou explodido. A montagem dos pacotes pode agregar classes oriundas do classpath, do sistema de arquivos ou de URLs remotas. Tradicionalmente, em Java a montagem de pacotes é realizada através de ferramentas como jar, Ant ou Maven. Mas, o ShrinkWrap oferece mais uma alternativa, no intuito de facilitar a criação de pacotes "virtuais" para a implantação de códigos de teste. O tópico "Exemplos de uso do ShrinkWrap" apresenta um pouco mais sobre o uso desta ferramenta.

Continuando nossa análise, notamos que um uma referência a um componente EJB é injetada pelo uso da anotação @EJB. A listagem 12 nos apresenta a declaração desta referência na linha 6. O interessante é que podemos notar que a interface TemperatureConverter é local. Logo, o teste deste componente não seria possível se a JVM do executor do teste não fosse a mesma que a em execução pelo contêiner. Ou seja, para que o teste pudesse ser executado, ele deveria rodar obrigatoriamente na mesma JVM que roda o contêiner. Hoje em dia, com o advento do JEE 6, podemos ter contêineres embutidos (e muito leves) que nos facilitam ainda mais os testes. O Arquillian também trata destes casos. Mas, especificamente neste exemplo, o contêiner é remoto (roda na JVM que executa o JBoss AS) e o código de teste rodado então numa JVM remota. Quem realiza a comunicação entre a JVM que está executando o Maven e a JVM em que os testes são realizados, é o Arquillian.

A listagem 13 apresenta a interface com.acme.ejb.TemperatureConverter e a listagem 14 mostra-nos a sua implementação (com.acme.ejb.TemperatureConverterBean) como um componente EJB que não guarda informações de estado (Stateless). Ambas são empacotadas pelo ShrinkWrap nas linhas 10 e 11 da listagem 12 no pacote test.jar. No carregamento do TestCase, o ShrinkWrap também é responsável pela implantação (deploy) deste pacote no contêiner. E, ao final de sua execução do procedimento contrário, ou seja, a desimplantação (undeploy).

Listagem 13. com.acme.ejb.TemperatureConverter 1 @Local
2 public interface TemperatureConverter {
3    double convertToCelsius(double f);
4    double convertToFarenheit(double c);
5    boolean isTransactional();
6 }

Listagem 14. com.acme.ejb.TemperatureConverterBean  1 public @Stateless class TemperatureConverterBean
 2       implements TemperatureConverter {
 3
 4    @Resource EJBContext ctx;
 5
 6    public double convertToCelsius(double f) {
 7       return ((f - 32) * 5 / 9);
 8    }
 9
10    public double convertToFarenheit(double c) {
11       return ((c * 9 / 5) + 32);
12    }
13
14    public boolean isTransactional() {
15       ctx.setRollbackOnly();
16       return ctx.getRollbackOnly();
17    }
18 }

O último teste executado cujo resultado é apresentada na listagem 08, linha 61, é o do TestCase com.acme.ejb.InjectionTestCase. Seu código é apresentado na listagem 15 e o seu papel é verificar se a injeção de uma referência obtida pela anotação @EJB é válida.

Trabalhando com EJBs, sabemos que a referência para o componente GreetingManagerBean pode ser injetada através da anotação @EJB. O TestCase inclui a referência greetingManager que é injetada através desta anotação. O método shouldBeAbleToInjectEJB(), por sua vez, confia que esta referência foi corretamente injetada para realizar a asserção que verifica se o retorno do método greet é igual ao esperado.

Mais uma vez, os aspectos relevantes na codificação deste TestCase, com relação ao uso do ShrinkWrap e do Arquillian, são: a anotação @RunWith e o método estático createDeployment anotado com @Deployment.

Antes da execução dos métodos de teste (anotados com @Test) em InjectionTestCase, o Arquillian solicitará ao ShrinkWrap a geração do pacote test.jar através da adição do componente GreetingManagerBean e de sua interface. Isto é realizado pelo método anotado com @Deployment. Em seguida, irá implantar o pacote e, após detectar o sucesso desta tarefa, executará os métodos de teste. Na execução do TestCase, as referências anotadas com @EJB serão apropriadamente injetadas.

O código da Interface GreetingManager e da implementação do componente EJB GreetingManagerBean é apresentado respectivamente pelas listagem 16 e listagem 17.

Listagem 15. com.acme.ejb.InjectionTestCase  1 @RunWith(Arquillian.class)
 2 public class InjectionTestCase
 3 {
 4    @Deployment
 5    public static JavaArchive createDeployment() {
 6       return ShrinkWrap.create("test.jar", JavaArchive.class)
 7                .addClasses(
 8                      GreetingManager.class,
 9                      GreetingManagerBean.class);
10    }
11   
12    @EJB
13    private GreetingManager greetingManager;
14   
15    @Test
16    public void shouldBeAbleToInjectEJB() throws Exception {
17       
18       String userName = "Devoxx";
19       
20       Assert.assertEquals(
21             "Hello " + userName,
22             greetingManager.greet(userName));
23    }
24 }

Listagem 16. com.acme.ejb.GreetingManager 1 public interface GreetingManager
2 {
3    String greet(String userName);
4 }

Listagem 17. com.acme.ejb.GreetingManagerBean  1 @Local(GreetingManager.class)
 2 @Stateless
 3 public class GreetingManagerBean implements GreetingManager
 4 {
 5   
 6    public String greet(String userName)
 7    {
 8       return "Hello " + userName;
 9    }
10 }

Testando injeção de recursos

O segundo teste executado pelo JUnit e cuja saída é apresentada na listagem 08 (linha 48), é o TestCase com.acme.resource.InjectionTestCase. Ele demonstra que a injeção de recursos, anotados com @Resource, também pode ser averiguada através do Arquillian. O código deste TestCase é apresentado na listagem 17.

Listagem 17. com.acme.resource.InjectionTestCase  1 @RunWith(Arquillian.class)
 2 public class InjectionTestCase
 3 {
 4    @Deployment
 5    public static JavaArchive createDeployment() {
 6       return ShrinkWrap.create("test.jar", JavaArchive.class);
 7    }
 8   
 9    @Resource(mappedName = "java:/Mail") Session mailSession;
10   
11    @Test
12    public void shouldBeAbleToInjectResource() throws Exception {
13       
14       Assert.assertNotNull(mailSession);
15    }
16 }

Observe que neste exemplo, shouldBeAbleToInjectResource verifica se a referência mailSession, para javax.mail.Session e mapeada para o nome java:/Mail no servidor JNDI, não é nula.

Testando JMS

O terceiro TestCase executado na listagem 08 (linha 50) é o com.acme.jms.InjectionTestCase. Seu código fonte é apresentado pela listagem 18 e demonstra que como o Arquillian também pode ser utilizado para validar componentes JMS.

Listagem 18. com.acme.jms.InjectionTestCase  1 @RunWith(Arquillian.class)
 2 public class InjectionTestCase
 3 {
 4    @Deployment
 5    public static JavaArchive createDeployment() {
 6       return ShrinkWrap.create("test.jar", JavaArchive.class)
 7             .addClasses(
 8                   MessageEcho.class,
 9                   QueueRequestor.class);
10    }
11   
12    @Resource(mappedName = "/queue/DLQ"
13    private Queue dlq;
14   
15    @Resource(mappedName = "/ConnectionFactory"
16    private ConnectionFactory factory;
17   
18    @Test
19    public void shouldBeAbleToSendMessage() throws Exception {
20       
21       String messageBody = "ping";
22       
23       Connection connection = null;
24       try
25       {
26          connection = factory.createConnection();
27          Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
28          QueueRequestor requestor = new QueueRequestor((QueueSession)session, dlq);
29
30          connection.start();
31         
32          Message request = session.createTextMessage(messageBody);
33          Message response = requestor.request(request, 5000);
34         
35          Assert.assertEquals(
36                "Should have responded with same message",
37                messageBody,
38                ((TextMessage)response).getText());
39       }
40       finally
41       {
42          connection.close();
43       }
44    }
45 }

As referências para a fila e para a fábrica de conexões são injetadas pelas anotações @Resource. O método de teste shouldBeAbleToSendMessage é responsável pelas seguintes tarefas: 1) recuperar uma conexão; 2) criar uma sessão; 3) criar um objeto requestor, responsável por obter uma resposta para uma mensagem enviada a uma fila informada; 4) iniciar a conexão; 5) criar uma mensagem de texto; 6) enviar a mensagem e aguardar uma resposta para ela em até 5000ms; e, por fim, 7) verificar se a resposta obtida foi a mesma que a enviada através de Assert.assertEquals(...);

As classes empacotadas pelo método createDeployment são MessageEcho (listagem 19) e QueueRequestor (listagem 20).

Listagem 19. com.acme.ejb.MessageEcho  1 @MessageDriven(activationConfig = {
 2       @ActivationConfigProperty( propertyName = "destination", propertyValue = "queue/DLQ"),
 3       @ActivationConfigProperty( propertyName = "destinationType", propertyValue = "javax.jms.Queue")
 4 })
 5 public class MessageEcho implements MessageListener
 6 {
 7
 8    @Resource(mappedName = "java:/ConnectionFactory")
 9    private ConnectionFactory factory;
10   
11    public void onMessage(Message msg)
12    {
13       try 
14       {
15          System.out.println("received " + msg.getJMSMessageID());
16         
17          Connection connection = factory.createConnection();
18          Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
19          MessageProducer producer = session.createProducer(msg.getJMSReplyTo());
20          producer.send(msg);
21          producer.close();
22          session.close();
23          connection.close();
24       }
25       catch (Exception e) 
26       {
27          throw new RuntimeException("Could not reply to message", e);
28       }
29    }
30 }

O MessageEcho é um componente EJB, do tipo Message Driven Bean (MDB). Este componente irá aguardar mensagens que chegarão pela fila nomeada queue/DLQ. Quando mensagens chegarem, o componente irá: 1) apresentar no console do servidor o ID da mensagem; 2) recuperar uma conexão; 3) criar uma sessão; 4) criar um produtor de mensagens para a fila que deve receber mensagens de resposta para a mensagem enviada; 5) enviar a mensagem recebida, como a mensagem de resposta; 6) liberar recursos (métodos close);

Listagem 20. com.acme.util.jms.QueueRequestor  1 public class QueueRequestor {
 2
 3    QueueSession session; // The queue session the queue belongs to.
 4    Queue queue; // The queue to perform the request/reply on.
 5    TemporaryQueue tempQueue;
 6    QueueSender sender;
 7    QueueReceiver receiver;
 8
 9    /**
10     * Constructor for the <CODE>QueueRequestor</CODE> class.
11     *
12     * <P>
13     * This implementation assumes the session parameter to be non-transacted,
14     * with a delivery mode of either <CODE>AUTO_ACKNOWLEDGE</CODE> or
15     * <CODE>DUPS_OK_ACKNOWLEDGE</CODE>.
16     *
17     * @param session
18     *           the <CODE>QueueSession</CODE> the queue belongs to
19     * @param queue
20     *           the queue to perform the request/reply call on
21     *
22     * @exception JMSException
23     *               if the JMS provider fails to create the
24     *               <CODE>QueueRequestor</CODE> due to some internal error.
25     * @exception InvalidDestinationException
26     *               if an invalid queue is specified.
27     */
28
29    public QueueRequestor(QueueSession session, Queue queue) throws JMSException
30    {
31       this.session = session;
32       this.queue = queue;
33       tempQueue = session.createTemporaryQueue();
34       sender = session.createSender(queue);
35       receiver = session.createReceiver(tempQueue);
36    }
37
38    /**
39     * Sends a request and waits for a reply. The temporary queue is used for the
40     * <CODE>JMSReplyTo</CODE> destination, and only one reply per request is
41     * expected.
42     *
43     * @param message
44     *           the message to send
45     *
46     * @return the reply message
47     *
48     * @exception JMSException
49     *               if the JMS provider fails to complete the request due to
50     *               some internal error.
51     */
52
53    public Message request(Message message) throws JMSException
54    {
55       return request(message, 0);
56    }
57
58    public Message request(Message message, int wait) throws JMSException
59    {
60       message.setJMSReplyTo(tempQueue);
61       sender.send(message);
62       return (receiver.receive(wait));
63    }
64
65    /**
66     * Closes the <CODE>QueueRequestor</CODE> and its session.
67     *
68     * <P>
69     * Since a provider may allocate some resources on behalf of a
70     * <CODE>QueueRequestor</CODE> outside the Java virtual machine, clients
71     * should close them when they are not needed. Relying on garbage collection
72     * to eventually reclaim these resources may not be timely enough.
73     *
74     * <P>
75     * Note that this method closes the <CODE>QueueSession</CODE> object passed
76     * to the <CODE>QueueRequestor</CODE> constructor.
77     *
78     * @exception JMSException
79     *               if the JMS provider fails to close the
80     *               <CODE>QueueRequestor</CODE> due to some internal error.
81     */
82
83    public void close() throws JMSException
84    {
85
86       // publisher and consumer created by constructor are implicitly closed.
87       session.close();
88       tempQueue.delete();
89    }
90  }

O QueueRequestor é um POJO simples, auxiliar do TestCase, cujas funções são: 1) em seu construtor: 1.1) guardar as referências para a sessão e para a fila que será utilizada para o envio de mensagens; 1.2) criar uma fila temporária que será utilizada para armazenar respostas de retorno para as mensagens enviadas; 1.3) criar objetos para o envio e para a recepção de mensagens; 2) no método request(message, wait): 2.1) ajustar a fila de respostas da mensagem que será enviada para a fila temporária; 2.2) enviar a mensagem; 2.3) aguardar até wait ms pelo retorno da mensagem de resposta;

Quando o componente MessageEcho recebe uma mensagem enviada pelo TestCase através de seu auxiliar QueueRequestor, ele simplesmente apresenta a mensagem no console do servidor, cria uma nova mensagem com o mesmo conteúdo da mensagem recebida e a envia para a fila temporária criada por QueueRequestor. O TestCase, por sua vez, aguarda o prazo de 5000ms para receber uma mensagem de retorno do QueueRequestor e verificar se o que o conteúdo recebido é o mesmo que foi enviado. É óbvio que se o texto da mensagem recebida não for o mesmo, o teste gerará uma falha.

Para explorar um pouco mais sobre o uso do Arquillian para testes de componentes JMS, um exemplo extra, que demonstra testes do módulo JMS do Seam 3 pode ser encontrado na página http://community.jboss.org/wiki/Seam3JMSModuleTestingwithArquillian.

Testando EJB 3.1

A linha 52 da listagem 08 apresenta a execução de um TestCase para um componente do tipo EJB, na versão 3.1. O código do TestCase com.acme.ejb31.NoInterfaceEJBTestCase é apresentado na listagem 21 e a classe testada com.acme.ejb31.NoInterfaceEJB pode ser visualizada na listagem 22.

Listagem 21. com.acme.ejb31.NoInterfaceEJBTestCase  1 @RunWith(Arquillian.class)
 2 public class NoInterfaceEJBTestCase
 3 {
 4    @Deployment
 5    public static JavaArchive createDeployment()
 6    {
 7       return ShrinkWrap.create("test.jar", JavaArchive.class)
 8                .addClass(NoInterfaceEJB.class);
 9    }
10   
11    @EJB
12    private NoInterfaceEJB ejb;
13   
14    @Test
15    public void shouldBeAbleToInjectNoInterfaceEJBs() throws Exception
16    {
17       Assert.assertNotNull(
18             "Verify that the ejb was injected",
19             ejb);
20       
21       Assert.assertEquals(
22             "Verify that the ejb returns correct value",
23             "Hey",
24             ejb.hello());
25    }
26 }

Listagem 22. com.acme.ejb31.NoInterfaceEJB 1 @Stateless
2 public class NoInterfaceEJB
3 {
4    public String hello() 
5    {
6       return "Hey";
7    }
8 }

A não obrigatoriedade de um componente EJB ter que implemementar uma interface surgiu a partir da versão 3.1 de sua especificação. Este tipo de conceito facilita o desenvolvimento. Podemos notar, pela observação da classe NoInterfaceEJB (listagem 22), que ela não passa de um POJO, com a anotação @Stateless. O código do TestCase NoInterfaceEJBTestCase faz duas verificações no test shouldBeAbleToInjectNoInterfaceEJBs. A primeira delas, verifica se a referência ejb foi corretamente injetada. A segunda, se o método hello retorna o esperado.

Novamente, o papel do Arquillian é definido pelo uso da anotação @RunWith, na classe do TestCase, e pela anotação @Deployment, para o método estático createDeployment.

Testando CDI

O código da listagem 23 monta um pacote do tipo JAR, denominado test.jar. Criado pelo ShrinkWrap, este pacote irá conter a interface com.acme.cdi.random.Randon e, no diretório META-INF do pacote, será criado o arquivo beans.xml com o conteúdo <beans\>. Este último arquivo por sua vez, precisa ser criado para que o componente esteja de acordo com a especificação CDI.

Listagem 23. com.acme.cdi.random.RandomTestCase  1 @RunWith(Arquillian.class)^M
 2 public class RandomTestCase {^M
 3 ^M
 4         @Deployment^M
 5         public static JavaArchive createDeployment() {^M
 6                 return ShrinkWrap.create("test.jar", JavaArchive.class)^M
 7                                 .addPackage(^M
 8                                                 Random.class.getPackage()^M
 9                                 )^M
10                                 .addManifestResource(^M
11                                                 new ByteArrayAsset("<beans/>".getBytes()),^M
12                                                 ArchivePaths.create("beans.xml"));^M
13         }^M
14 ^M
15         @Inject @Random int randomNumber;^M
16         ^M
17         @Test^M
18         public void shouldRun() throws Exception ^M
19         {^M
20            Assert.assertTrue(randomNumber < 101);^M
21         }^M
22 }^M

Listagem 24. com.acme.cdi.random.Random 1 @Qualifier^M
2 @Retention(RetentionPolicy.RUNTIME)^M
3 @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})^M
4 public @interface Random {}^M

Este TestCase utiliza a implementação de referência do CDI 1.0 (conhecida por Weld) para obter, por injeção de dependências realizada pelo contêiner, a referências para o objetos randoNumber.

TODO:

Testando Servlet

TODO

Executando testes com Arquillian/TestNG

Testando JPA

Para testar componentes JPA é necessário um ambiente com um SGBD configurado e um persistence unit. No caso do JBoss AS, este servidor, quando em execução, também inicia uma instância do HSQLDB, seu SGBD embutido. Ele também já deixa configurado um datasource default, que é nomedado java:/DefaultDS no servidor JNDI do JBoss. No exemplo a seguir, o datasource utilizado será o default e as tabelas serão automaticamente criadas quando o persitence unit for iniciado. Também, neste exemplo, o TestCase é executado pelo TestNG e não pelo JUnit.

O código do persiscente unit é apresentado na listagem 27.

Listagem 27. com.acme.jpa.user-test-persistence.xml  1 <?xml version="1.0" encoding="UTF-8"?>
 2 <persistence version="1.0"
 3    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
 5    <persistence-unit name="Domain" transaction-type="JTA">
 6       <provider>org.hibernate.ejb.HibernatePersistence</provider>
 7       <jta-data-source>java:/DefaultDS</jta-data-source>
 8       <class>com.acme.jpa.User</class>
 9       <exclude-unlisted-classes>true</exclude-unlisted-classes>
10       <properties>
11          <property name="hibernate.hbm2ddl.auto" value="create-drop" />
12          <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
13       </properties>
14    </persistence-unit>
15 </persistence>

O TestCase, que pode ser visualizado na listagem 28, apresenta o dois métodos de teste: shouldBeAbleToStoreUser e shoudBeAbleToFindUser. O segundo método de teste aproveita uma característica do framework TestNG que possibilita que um teste dependa de outro para ser executado. Isto é especificado através do atributo dependsOnMethods na anotação @Test do método. Sendo assim, a execução do método de teste shouldBeAbleToFindUser depende da execução anterior do método shouldBeAbleToStoreUser.

A lógica codificada nos métodos de teste neste TestCase é bastante simples. O primeiro teste, shouldBeAbleToStoreUser, apesar de não efetuar nenhuma asserção, pode lançar um erro ao tentar inserir a entidade User (listagem 29). As operações relativas a inserção e a localização da entidade User são codificadas no Stateless SessionBean (SLSB) UserRepositoryBean (listagem 30), cuja interface é UserRepository (listagem 31). O segundo teste, shoudBeAbleToFindUser, obtém uma lista de usuários cujo primeiro nome foi o informado, valida se ela não é nula, se contém apenas um elemento e se os atributos lastName e fistName obtidos para este elemento são os que realmente eram os esperados.

Este TestCase deveria estar sendo executado com sucesso, se as anotações @Test não estivessem com o atributo enabled ajustado para false (desta forma, tais testes não são executados pelo TestNG na execução do TestCase). O problema que ocorre, documentado como ARQ-55 e ainda sem solução no Jira do Arquillian, é referente ao lançamento de uma exceção na execução do TestCase pelo TestNG quando utilizado o atributo dependsOnMethods para um @Test. Entretanto, se não há o uso deste atributo, o teste pode ser realizado com sucesso. Por exemplo, a documentação do Arquillian apresenta este mesmo exemplo, funcionando, remanejando o código escrito nos dois métodos de teste para um único.

Listagem 28. com.acme.jpa.UserRepositoryTestCase  1 @Test(groups = "integration")
 2 public class UserRepositoryTestCase extends Arquillian {
 3    @Deployment
 4    public static JavaArchive createDeployment() {
 5       return Archives.create("test.jar", JavaArchive.class)
 6                .addPackage(
 7                      User.class.getPackage())
 8                .addManifestResource(
 9                      "com/acme/jpa/user-test-persistence.xml",
10                      ArchivePaths.create("persistence.xml"));
11    }
12   
13    private static final String FIRST_NAME = "first-name";
14    private static final String LAST_NAME = "last-name";
15   
16    @EJB
17    private UserRepository userRepository;
18   
19    @Test(enabled = false// https://jira.jboss.org/jira/browse/ARQ-55
20    public void shouldBeAbleToStoreUser() throws Exception
21    {
22       userRepository.store(new User(FIRST_NAME, LAST_NAME));
23    }
24   
25    @Test(dependsOnMethods = "shouldBeAbleToStoreUser", enabled = false// https://jira.jboss.org/jira/browse/ARQ-55
26    public void shouldBeAbleToFindUser() throws Exception
27    {
28       List<User> users  = userRepository.getByFirstName(FIRST_NAME);
29       
30       Assert.assertNotNull(users);
31       Assert.assertTrue(users.size() == 1);
32       
33       Assert.assertEquals(users.get(0).getLastName(), LAST_NAME);
34       Assert.assertEquals(users.get(0).getFirstName(), FIRST_NAME);
35    }
36 }

Listagem 29. com.acme.jpa.User  1 @Entity
 2 @Table
 3 public class User implements Serializable {
 4    private static final long serialVersionUID = 1L;
 5
 6    @Id
 7    private String id;
 8    private String firstName;
 9    private String lastName;
10   
11    protected User() {
12    }
13   
14    public User(String firstName, String lastName) {
15       this.id = UUID.randomUUID().toString();
16       this.firstName = firstName;
17       this.lastName = lastName;
18    }
19   
20    // métodos get e set ...
21 }

Listagem 30. com.acme.jpa.UserRepositoryBean  1 @Local(UserRepository.class)
 2 @Stateless
 3 public class UserRepositoryBean implements UserRepository {
 4    @PersistenceContext(unitName = "Domain")
 5    private EntityManager entityManager;
 6   
 7    @SuppressWarnings("unchecked")
 8    @Override
 9    public List<User> getByFirstName(String firstName) {
10       return entityManager.createQuery("from User user where user.firstName = :firstName")
11                            .setParameter("firstName", firstName)
12                            .getResultList();
13    }
14
15    @Override
16    public void store(User user) {
17       entityManager.persist(user);
18    }
19 }

Listagem 31. com.acme.jpa.UserRepository 1 public interface UserRepository {
2    List<User> getByFirstName(String firstName);
3    void store(User user);
4 }

Exempos de uso do ShrinkWrap

O ShrinkWrap também é capaz de, como citado, gerar um WAR, exportar o conteúdo de um arquivo no formato zip ou no formato explodido, importar o conteúdo de um arquivo zip ou de um diretório. Contêineres embutidos, como o JBoss Embedded, também são capazes de implantar um objeto do tipo Arquive, oriundo do ShrinkWrap.

Geração de um arquivo WAR com o ShrinkWrap

TODO

Exportação para um arquivo, no formato zip

TODO

Exportação para um diretório, no formato explodido

TODO

Importação para um arquivo, a partir de um zip

TODO

Importação para um arquivo, a partir de um diretório no formato explodido

TODO

Implantação direta de um arquivo para um contêiner

TODO

Executando testes através do TestNG

Considerações finais

O Arquillian pode ser facilmente integrado a qualquer projeto Maven. Para mais detalhes sobre isto, observe os POMs dos exemplos discutidos e a documentação do projeto. Além disto, esta documentação apresenta como rodar os TestCases discutidos neste tutorial utilizando o Eclipse e o plugin m2eclipse. Também demonstra como realizar os testes em contêineres diferentes do JBoss AS. Explore os exemplos e a documentação!

Além da utilização do Arquillian e do ShrinkWrap para a realização de testes nos vários tipos de componentes discutidos neste tutorial, no Wiki da comunidade JBoss, o artigo "Testing a JMX Portable Extension for CDI" apresenta um exemplo de utilização do Arquillian para testar componentes JMX. O artigo "Using Arquillian to test a possible bug in Weld" demonstra a utilização dele para a averiguação da existência de um bug no Weld. Por fim, a URL http://www.diigo.com/user/jbosstesting/arquillian aponta para uma página que agrega links para diversos outros materiais publicados na Internet.

A comunidade JBoss envolvida na criação deste produto é bastante participativa. A liderança do projeto Arquillian neste momento é de Aslak Knutsen, autor dos exemplos apresentados. Eu, particularmente, tive a oportunidade de conversar pessoalmente com o Pete Muir, fundador do projeto, no evento JBossInBossa, ocorrido nos dias 7 e 8 de maio. Na ocasião, eu apresentei a ele um draft deste tutorial e nós nos sentamos a mesma mesa para que eu pudesse lhe mostrar um problema de compilação que eu estava sofrendo nos fontes do Arquillian disponíveis em trunk. Tentamos resolvê-lo juntos mais não foi possível. No dia 10 de maio, a discussão "Build Arquillian With Nexus Repo" foi criada por Andrew Rubinger comentando sobre o problema que eu havia passado ao Pete Muir durante o evento. No momento em que finalizo este tutorial, posso afirmar que a construção dos fontes em trunk pode ser realizada através do Maven 3 (como demonstrado) e que o Arquillian também já pode ser utilizado para a realização de testes com vários outros contêineres além da versão 6.0.0.M3 do JBoss AS, tanto de maneira remota, quanto embutida.

Tanto o Arquillian quanto o ShrinkWrap vem sendo apresentados por membros da comunidade JBoss em vários eventos, de grande repercussão, elevando a sua quantidade de usuários. Alguns dos últimos materiais publicados sobre estas ferramentas estão em http://github.com/arquillian-sandbox. Eu posso dizer que ele realmente traz facilidades na criação de testes integrados e que também já passei a utilizá-lo em alguns projetos. Faça o mesmo! Seus testes integrados ficarão muito mais simples com o auxílio destas ferramentas.

Referências

Vídeo de execução do tutorial

TODO