Testes em aplicações JEE: Montando sua infra de testes automatizados

Preview:

DESCRIPTION

A 4Linux foi uma das patrocinadoras do Just Java 2012 que ocorreu nos dias 18 e 19/05/2012. Além do patrocínio, a equipe 4Linux foi responsável pela apresentação da palestra: Testes em aplicações Java EE: Montando sua infra de testes automatizados.

Citation preview

2 / 44www.4linux.com.br

Experiência em missão crítica de missão crítica

Pioneira no ensino de Linux à distância

Parceira de treinamento IBM

Primeira com LPI no Brasil

+ de 30.000 alunos satisfeitos

Reconhecimento internacional

Inovação com Hackerteen e Boteconet

Testes em aplicações JEE:Testes em aplicações JEE: Montando sua infra de testes automatizadosMontando sua infra de testes automatizados

4 / 44www.4linux.com.br

AgendaAgenda

● Porque testar?● Escrevendo testes com mais valor● Problemas testando componentes JEE● Como integrar containers aos testes.

5 / 44www.4linux.com.br

Porque automatizar testes?Porque automatizar testes?

● Modinha...● Eficiência dos testes de regressão

● Você pode melhorar/refatorar o sistema com tranquilidade, se estragar você sabe instantaneamente que estragou

● Integração contínua pode te tirar do apuro● Você tem uma espécie de auditoria “vigiando” o funcionamento do

seu código, se falhar você saberá exatamente onde e porque falhou

6 / 44www.4linux.com.br

Problemas na hora de testar?Problemas na hora de testar?

● Mas te ensinaram como usar em situações do cotidiano?● Até que ponto testar com objetos fake e mocks?

● Difícil montar ambientes onde os testes podem ser executados

● O quanto de esforço é necessário para executar testes em ferramentas de build e IDEs ao mesmo tempo.

Segundo Bill Burke, mocks te dão uma falsa sensação de segurança, pois permitem distorcer os testes para que seu código passe.

7 / 44www.4linux.com.br

Problemas em testes de Problemas em testes de componentes JEEcomponentes JEE

● Ciclo chato de desenvolvimento

● Isolar o que deve ser testado● Para um componente ser testado, ele dependente de um

outro componente ou um serviço oferecidos pelo container.

● Exemplos:– Filas de mensageria– Transacionalidade– Segurança– Data Sources

Implementação Deploy Test Implementação

8 / 44www.4linux.com.br

Mas eu utilizo Ant e o Maven para executar os testes pra mim, moleza!

Ciclo chato de desenvolvimento

9 / 44www.4linux.com.br

Utilizando ferramentas de Utilizando ferramentas de buildbuild

● Ant, Maven:● Vantagem:

– Fácil de executar testes: mvn test (unitários) / mvn verify (unitários e integração)

– Fácil integração com servidor de integração contínua como Jenkins.

● Desvantagem:– Executa todos os testes de um vez. – (Maven) Os erros são somente mostrados nos

relátorios gerados– (Maven) não integra as suas fases de teste com a

IDE

10 / 44www.4linux.com.br

Vei, na boa! Minha aplicação nem usa EJB

11 / 44www.4linux.com.br

Para recursos simples da WebPara recursos simples da Web

● Sua aplicação não usa EJB, JTA, JMS?● Você não precisa testar em um servidor de aplicações FULL. ● Utilize só um container web, mais leve e mais fácil de

configurar.● Opções como o Jetty, Tomcat. Ambos possuem versão

embutidas, que pode ser adicionada aos testes.● Jetty é dividido em módulos e permite especificar quais deles

seram iniciaram com os testes.● Possuem plugins para Maven.● Possuem APIs que podem ser usadas dentro dos testes.

12 / 44www.4linux.com.br

Embedded JettyEmbedded Jetty

● Container de servlets leve● Possui uma API para configurar e iniciar

uma instância do container.

Server server = new Server(8080);Context root = new Context(server,"/agenda",Context.SESSIONS);

root.addServlet(new ServletHolder(new ContatoServlet()), "contatos");server.start();

13 / 44www.4linux.com.br

API do JettyAPI do Jetty

private static WebAppContext gerandoContextoAplicacao() throws Exception {WebAppContext context = new WebAppContext();context.setWar(new File(URI_DO_WAR).getAbsolutePath());context.setContextPath(“/aplicacao-do-jjustjava/”);

context.setConfigurations(new Configuration[] {new AnnotationConfiguration(), new JettyWebXmlConfiguration(),new WebInfConfiguration(), new WebXmlConfiguration(),new TagLibConfiguration(), new MetaInfConfiguration(),new PlusConfiguration(), new FragmentConfiguration(),new EnvConfiguration() });

return context;}

@BeforeClasspublic static void inicializar() throws Exception {

server = new Server(SERVER_PORT);WebAppContext contexto = gerandoContextoAplicacao();

server.setHandler(contexto);server.start();

}

A API do Jetty permite que você especifique os módulos que são importantes para você, no caso, em seus testes.

Mais dependências para utilizar JSPs: ● ant-1.6.5.jar● core-3.1.1.jar● jsp-2.1.jar● jsp-api-2.1.jar

14 / 44www.4linux.com.br

Tomcat EmbeddedTomcat Embedded

● Mais fácil de iniciar que o Jetty por todos os módulos já virem configurados para iniciar.

// Configura container de servlet embutidoTomcat servidor = new Tomcat();

// Aponta o diretorio temporario para que o container necessitaservidor.setBaseDir("target/temp");

// Especifica a porta onde irá funcionarservidor.setPort(8081);

// Inicia servidorservidor.start();

<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>7.0.27</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>7.0.27</version> <scope>test</scope> </dependency>

15 / 44www.4linux.com.br

API do Tomcat : Adicionando API do Tomcat : Adicionando ConteúdoConteúdo

servidor.addWebapp("/carrinho", enderecoDoWar); Adiciona o WAR ao contexto /carrinho

File diretorioWebapp = new File("src/main/webapp");Context contexto = servidor.addWebapp("/", diretorioWebapp.getAbsolutePath());

servidor.addServlet("/", "LeilaoServlet", new LeilaoServlet());contexto.addServletMapping("/leilao", "LeilaoServlet");

Mapeando o Servlet adicionado

Você pode associar um WAR do file system a um contexto utilizando o método addWebapp

Também é possível tornar um diretório, o contexto da aplicação, adicionando servlets aoContainer e mapeando-os no contexto.

16 / 44www.4linux.com.br

Mas os como eu forneço pacotes com as classes para os containers dentro dos meus testes?

17 / 44www.4linux.com.br

JBoss ShrinkWrapJBoss ShrinkWrap

● Projeto que ajuda automatizar e personalizar a criação de pacotes Java

● EAR, WAR, JAR ou também qualquer outro Zip.● A maioria dos container embutidos permitem especificar um

pacote para registrado inserido em um contexto.

WebArchive warAplicacao = ShrinkWrap.create(WebArchive.class, “livraria.war”).addClasses(Cliente.class, CartaoCreditoInfo.class).addPackage(“fourlinux.justjava.store.dao”);.setWebXML(“/src/test/resources/web.xml”);

18 / 44www.4linux.com.br

JBoss ShrinkWrapJBoss ShrinkWrap

<project> ... <dependencies> ... <dependency> <groupId>org.jboss.shrinkwrap</groupId> <artifactId>shrinkwrap-api</artifactId> </dependency> <dependency> <groupId>org.jboss.shrinkwrap</groupId> <artifactId>shrinkwrap-impl-base</artifactId> <scope>test</scope> </dependency> ... </dependencies> ... </project>

<repository> <id>jboss-public-repository-group</id> <name>JBoss Public Maven Repository Group</name> <url>https://repository.jboss.org/nexus/content/groups/public/</url> </repository>

19 / 44www.4linux.com.br

Usando o ShrinkWrapUsando o ShrinkWrap

// Exportando WAR:WebArchive war = ShrinkWrap.create(WebArchive.class);

war.addPackages(true, "fourlinux.justjava");

war.as(ZipExporter.class).exportTo(new File(“/target/shrinkwrap/aplicacao.war”));

Adiciona todas as classesdo seguinte pacote recursivamente

Cria um arquivo zip para o conteúdo indicado

// Embedded Jetty WebAppContext context = new WebAppContext();context.setWar(new File(uriDoWar).getAbsolutePath());;

20 / 44www.4linux.com.br

Usando ShrinkWrap + MavenUsando ShrinkWrap + Maven

● As vezes seu projeto está dividido em submódulos e um componente que está sendo testado depende de um módulo declarado no POM

● Ou você precisa adicionar um driver JDBC no pacote para testar

ShrinkWrap ResolverMavenDependencyResolver resolver = DependencyResolvers.use(

MavenDependencyResolver.class);

MavenDependencyBuilder dominio = resolver.artifact("soujava.justjava:livraria-dominio:1.0");

carrinho.addAsLibraries(dominio.resolveAs(GenericArchive.class));

21 / 44www.4linux.com.br

MAS MEU PROJETO POSSUI EJBs, COMO TESTO?

22 / 44www.4linux.com.br

Apache OpenEJBApache OpenEJB

● Container EJB● Desde 1999● Como pode ser utilizado nos testes?

● Diferencial: Leve e rápido● Pode ser iniciado dentro dos testes.● Suporta as versões 3.1, 3.0, 2.1, 2.0, 1.1● Simples configuração e inicialização.● Simplicica os ciclos de desenvolvimento e teste.

23 / 44www.4linux.com.br

Configurando OpenEJB com Configurando OpenEJB com MavenMaven

● É utilizado como uma simples dependência.<project>

...<dependency>

<groupId>org.apache.openejb</groupId><artifactId>javaee-api</artifactId><scope>provided</scope><version>6.0-3-SNAPSHOT</version>

</dependency><dependency>

<groupId>org.apache.openejb</groupId><artifactId>openejb-core</artifactId><scope>test</scope><version>4.0.0-beta-1</version>

</dependency></project>

API

Container

<dependency><groupId>org.apache.openejb</groupId><artifactId>openejb-core-hibernate</artifactId><version>4.0.0-beta-1</version><type>pom</type>

</dependency>

Preciso de suporte a JPA, o que faço?

OpenEJB com Hibernate

24 / 44www.4linux.com.br

Iniciando container Iniciando container

● O container pode ser iniciado dentro dos testes, seja a cada teste ou em um setup para todos os testes.

@Beforepublic void inicializacao() {

Properties props = new Properties();props.put(Context.INITIAL_CONTEXT_FACTORY,

"org.apache.openejb.client.LocalInitialContextFactory");Context context = new InitialContext(props);

}

@Beforepublic void inicializacao() {

EjbContainer container = EjbContainer.createEjbContainer();Context context = container.getContext();

}

<= EJB 3.0

>= EJB 3.1

25 / 44www.4linux.com.br

Testando SessionBeansTestando SessionBeans

@Statelesspublic class ServicoPagamento {

public void cobrar(double valor, CartaoCreditoInfo ccInfo) {...

}}@Testpublic class ServiceoPagamentoIT {

@Test(expected=IllegalArgumentException.class)public void valorNegativoLancaExcecao() {

Context contexto = EjbContainer.createEjbContainer().getContext();ServicoPagamento sp = (ServicoPagamento)

contexto.lookup(“java:global/projeto/ServicoPagamento”);sp.cobrar(-5, ccInfo);

}} Utilizando o JNDI para recuperar componentes

como session beans

Provê um JNDI padrãoque você pode customizá-lo.

O OpenEJB caça sua aplicação por componentes JavaEE que podem ser de sua responsabilidade.

26 / 44www.4linux.com.br

Configurando RecursosConfigurando Recursos

public class OrderProcessorIT {@Testpublic void processOrderAddItensToDatabase() {

Properties props = new Properties(); props.put("shopDatabase", "new://Resource?type=DataSource"); props.put("shopDatabase.JdbcDriver", "org.hsqldb.jdbcDriver"); props.put("shopDatabase.JdbcUrl", "jdbc:hsqldb:mem:shopdb");

Context contexto = EjbContainer.createEjbContainer(props).getContext();OrderProcessor orders = (OrderProcessor)

contexto.lookup(“java:global/projeto/OrderProcessor”);}

}

@Statelesspublic class OrderProcessor {

@ResourceDataSource dataSource;

...}

Configurando recursos como DataSource, filas, tópicos, fábricas de conexões...

27 / 44www.4linux.com.br

Ligando o teste no containerLigando o teste no container

● Há momentos onde é mais pratico permitir que o teste tenha acesso aos recursos através de injeção de dependência.public class GameContextIT {

@PersistenceContext(unitName=”gameunit”)private EntityManager em;

@Resourceprivate Queue filaJogadores;

@EJBGameController controller;…public SetUp() throws Exception {

EJBContainer container = EJBContainer.createEJBContainer();container.getContext().bind(“inject”, this);

}}

Acesso a todos os recursos do Container, como filas, datasources, ejbs e ao contexto cdi

28 / 44www.4linux.com.br

Mais eu quero testar em um servidor de aplicação

completo, inútil!

29 / 44www.4linux.com.br

Testando na real com Testando na real com JBoss ArquillianJBoss Arquillian

● Facilmente extensível.● Ajuda a diminui o esforço para fazer testes em qualquer

tipo de container, seja remoto ou embutido.● Gerencia todo o ciclo de vida do container e da aplicação

sendo executada nele.● Com ajuda do ShrinkWrap, permite montar pacotes

personalizados para deploy.● Permite executar testes dentro e fora do container.

30 / 44www.4linux.com.br

Arquillian + MavenArquillian + Maven

Novamente adicionar repositório Jboss:http://repository.jboss.org/nexus/content/groups/public

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

</dependency>

Importando dependências do Arquillian

<dependency><groupId>org.jboss.arquillian.junit</groupId><artifactId>arquillian-junit-container</artifactId><version>1.0.0.Final</version>

</dependency>

Integração com Junit ou TestNG

Falta a extensão do container...

31 / 44www.4linux.com.br

Arquillian + MavenArquillian + Maven

Extensões para

● JBoss● Tomcat● Glassfish● Resin● Weld● OSGI● CloudBees● OpenEJB● Jetty● Google AppEngine● Spring

Arquillian com Glassfish<dependency>

<groupId>org.jboss.arquillian.container</groupId><artifactId>arquillian-glassfish-embedded-3.1</artifactId><version>1.0.0.CR3</version><scope>test</scope>

</dependency><dependency>

<groupId>org.glassfish.main.extras</groupId><artifactId>glassfish-embedded-all</artifactId><version>3.1.2</version><scope>test</scope>

</dependency>

Glassfish Embutido

Extensão para glassfish

32 / 44www.4linux.com.br

JBoss ArquillianJBoss Arquillian

● Do que o Arquillian precisa?● Os testes devem funcionar com

@RunWith(Arquillian.class)● Pelo menos um método anotado com

@Test● Um método estático que entregue um pacote Java montado

pelo ShrinkWrap● Um container onde ele irá instalar o pacote.

33 / 44www.4linux.com.br

Teste com CDI usando Teste com CDI usando ArquillianArquillian@RunWith(Arquillian.class)public class GerenciadorReservasTest {

@Injectprivate GerenciadorReservas reservas;

@Deploymentpublic static JavaArchive gerandoArquivoDeploy() {

JavaArchive arquivo = ShrinkWrap.create(JavaArchive.class);arquivo.addPackages(true, “fourlinux.justjava”);arquivo.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");return arquivo;

}

@Testpublic void reservaDeveGerarCobranca() throws Exception {

Cabine cabine = new Cabine(“Titanic”, “A12”, Tipo.LUXO, 340.00f);Usuario usuario = new Usuario("gabriel.ozeas1@gmail.com", "Gabriel Ozeas");

Cobranca cobranca = reservas.reservarCabine(new Cabine[]{cabine}, usuario);assertEquals(new Float(340), new Float(cobranca.getValor()));

}}

34 / 44www.4linux.com.br

Testando em Containers Testando em Containers RemotosRemotos

<dependency><groupId>org.jboss.as</groupId><artifactId>jboss-as-arquillian-container-remote</artifactId><version>7.1.1.Final</version><scope>test</scope>

</dependency><dependency>

<groupId>org.jboss.as</groupId><artifactId>jboss-as-controller-client</artifactId><version>7.1.1.Final</version><scope>test</scope>

</dependency>

Cliente JBoss Remoto

Utilizando containers remotos e gerenciados, faz com que eles iniciem em outra JVM, ou seja os logs e debug não estão disponiveis na JVM que iniciou os testes.

35 / 44www.4linux.com.br

Arquillian como ClienteArquillian como Cliente

@RunWith(Arquillian.class)public class GerenciadorReservasTest {

@Deployment(testable = false)public static JavaArchive gerandoArquivoDeploy() {

JavaArchive arquivo = ShrinkWrap.create(JavaArchive.class);arquivo.addPackages(true, “fourlinux.justjava”);arquivo.setWebXML(“src/main/webapp/WEB-INF/web.xml”);return arquivo;

}

@Testpublic void verificandoTotalItensInicial() throws Exception {

WebDriver navegador = new HtmlUnitDriver();navegador.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

navegador.get("http://localhost:8081/leilao");WebElement body = navegador.findElement(By.tagName("h1"));assertEquals("Histórico do item", body.getText());

}}

Testando UI com Selenium

Deploy no container, mas testa no cliente

Teste OUT Container

36 / 44www.4linux.com.br

Misturando In e Out ContainerMisturando In e Out Container

@RunWith(Arquillian.class)public class GerenciadorReservasTest {

@Injectprivate GerenciadorReservas reservas;

@Deploymentpublic static JavaArchive gerandoArquivoDeploy() {

JavaArchive arquivo = ShrinkWrap.create(JavaArchive.class);arquivo.addClasses(...);...

}

@Testpublic void reservaDeveGerarCobranca() throws Exception {

….Cobranca cobranca = reservas.reservarCabine(new Cabine[]{cabine}, usuario);assertEquals(new Float(340), new Float(cobranca.getValor()));

}

@Test@RunAsClientpublic void verificandoTotalItensInicial() throws Exception {

WebDriver navegador = new HtmlUnitDriver();navegador.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);...

}}

Teste será executado na JVM que inicou os testes

Teste será executado na JVM do container

37 / 44www.4linux.com.br

PLUS: InfinitestPLUS: Infinitest

● Customizando a IDE:● Teste continuo com Infinitest

– Ajuda a executar os testes dentro do Eclipse automaticamente– Verde, passou, Vermelho Falhou.– Permite criar filtros para escolher quais testes serão sempre

executados.– Recomendado para executar em testes de unidade, pois são

normalmente executados em millisegundos.

Testes que falharam

38 / 44www.4linux.com.br

Filtrando testes no InfinitestFiltrando testes no Infinitest

● O Infinitest permite que você filtre os testes que serão executados continuamente.

● Arquivo infinitest.filtersBloqueia todos os testes de integração:.*ITest

Bloqueia todos os testes de um pacote:org\.soujava\.justjava\..*

Bloqueia todos os teste do projeto:.*

39 / 44www.4linux.com.br

EmmaEmma

40 / 44www.4linux.com.br

EmmaEmma

41 / 44www.4linux.com.br

Cobertura dos TestesCobertura dos Testes

42 / 44www.4linux.com.br

Cobertura dos TestesCobertura dos Testes

43 / 44www.4linux.com.br

Chegaremos a 100% ???Chegaremos a 100% ???

➢ Nem tudo será testado➢ Ex: Gets e Setters!➢ Classes sem lógica de negócio /

domínio➢ O aumento do esforço para se chegar

a 100% pode não valer a pena!

44 / 44www.4linux.com.br

O que acontece na realidade?O que acontece na realidade?

➢ A maioria das empresas estabelece um patamar mínimo aceitável:➢ Ex. 80% de cobertura (somente um exemplo)!

➢ Os testes unitários e a verificação de cobertura são feitos automaticamente;➢ A verificação de cobertura pode ser configurada

para analisar apenas algumas classes ou pacotes ➢ Utilizar patamares diferentes para recusar o build

em cada classe ou pacote.