A empresa 4linux é pioneira no ensino de Linux à distância no Brasil, com mais de 30.000 alunos satisfeitos. Ela possui experiência em missões críticas, é parceira de treinamento da IBM e foi a primeira empresa no Brasil a oferecer certificação LPI.
Testes em aplicações JEE: Montando sua infra de testes automatizados
1.
2. 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
www.4linux.com.br 2 / 44
4. Agenda
● Porque testar?
● Escrevendo testes com mais valor
● Problemas testando componentes JEE
●
Como integrar containers aos testes.
www.4linux.com.br 4 / 44
5. 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
www.4linux.com.br 5 / 44
6. Problemas na hora de testar?
● Mas te ensinaram como usar em situações do cotidiano?
● Até que ponto testar com objetos fake e mocks?
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.
● 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.
www.4linux.com.br 6 / 44
7. Problemas em testes de
componentes JEE
● Ciclo chato de desenvolvimento
Implementação Deploy Test Implementação
● 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
www.4linux.com.br 7 / 44
8. Ciclo chato de desenvolvimento
Mas eu utilizo Ant e o Maven para executar
os testes pra mim, moleza!
www.4linux.com.br 8 / 44
9. Utilizando ferramentas de
build
● 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
www.4linux.com.br 9 / 44
10. Vei, na boa! Minha aplicação nem usa EJB
www.4linux.com.br 10 / 44
11. Para 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.
www.4linux.com.br 11 / 44
12. Embedded 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();
www.4linux.com.br 12 / 44
13. API do Jetty
A API do Jetty permite que você especifique os módulos
que são importantes para você, no caso, em seus testes.
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;
}
@BeforeClass
public static void inicializar() throws Exception {
Mais dependências para utilizar JSPs: server = new Server(SERVER_PORT);
● ant-1.6.5.jar
WebAppContext contexto = gerandoContextoAplicacao();
● core-3.1.1.jar
● jsp-2.1.jar server.setHandler(contexto);
● jsp-api-2.1.jar
server.start();
}
www.4linux.com.br 13 / 44
14. Tomcat Embedded
● Mais fácil de iniciar que o Jetty por todos os módulos já
virem configurados para iniciar.
// Configura container de servlet embutido
Tomcat servidor = new Tomcat();
// Aponta o diretorio temporario para que o container necessita
servidor.setBaseDir("target/temp");
// Especifica a porta onde irá funcionar <dependency>
servidor.setPort(8081); <groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
// Inicia servidor <version>7.0.27</version>
<scope>test</scope>
servidor.start(); </dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>7.0.27</version>
<scope>test</scope>
</dependency>
www.4linux.com.br 14 / 44
15. API do Tomcat : Adicionando
Conteúdo
Você pode associar um WAR do file system a um contexto
utilizando o método addWebapp
servidor.addWebapp("/carrinho", enderecoDoWar);
Adiciona o WAR ao
contexto /carrinho
Também é possível tornar um diretório, o contexto da aplicação, adicionando servlets ao
Container e mapeando-os no contexto.
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
www.4linux.com.br 15 / 44
16. Mas os como eu forneço pacotes com
as classes para os containers dentro
dos meus testes?
www.4linux.com.br 16 / 44
17. JBoss 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”);
www.4linux.com.br 17 / 44
19. Usando o ShrinkWrap
// Exportando WAR:
WebArchive war = ShrinkWrap.create(WebArchive.class);
war.addPackages(true, "fourlinux.justjava"); Adiciona todas as classes
do seguinte pacote recursivamente
war.as(ZipExporter.class).exportTo(new File(“/target/shrinkwrap/aplicacao.war”));
Cria um arquivo zip para o conteúdo indicado
// Embedded Jetty
WebAppContext context = new WebAppContext();
context.setWar(new File(uriDoWar).getAbsolutePath());;
www.4linux.com.br 19 / 44
20. Usando 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 Resolver
MavenDependencyResolver resolver = DependencyResolvers.use(
MavenDependencyResolver.class);
MavenDependencyBuilder dominio = resolver.artifact("soujava.justjava:livraria-dominio:1.0");
carrinho.addAsLibraries(dominio.resolveAs(GenericArchive.class));
www.4linux.com.br 20 / 44
21. MAS MEU PROJETO POSSUI EJBs,
COMO TESTO?
www.4linux.com.br 21 / 44
22. Apache 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.
www.4linux.com.br 22 / 44
23. Configurando OpenEJB com
Maven
● É utilizado como uma simples dependência.
<project>
... API
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>javaee-api</artifactId>
<scope>provided</scope>
<version>6.0-3-SNAPSHOT</version>
</dependency> Container
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-core</artifactId>
<scope>test</scope>
<version>4.0.0-beta-1</version>
</dependency>
</project>
Preciso de suporte a JPA, o que faço? <dependency>
<groupId>org.apache.openejb</groupId>
OpenEJB com Hibernate <artifactId>openejb-core-hibernate</artifactId>
<version>4.0.0-beta-1</version>
<type>pom</type>
</dependency>
www.4linux.com.br 23 / 44
24. Iniciando container
● O container pode ser iniciado dentro dos testes, seja a
cada teste ou em um setup para todos os testes.
@Before <= EJB 3.0
public void inicializacao() {
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
Context context = new InitialContext(props);
}
>= EJB 3.1
@Before
public void inicializacao() {
EjbContainer container = EjbContainer.createEjbContainer();
Context context = container.getContext();
}
www.4linux.com.br 24 / 44
25. Testando SessionBeans
@Stateless
public class ServicoPagamento {
public void cobrar(double valor, CartaoCreditoInfo ccInfo) {
...
}
} Provê um JNDI padrão
que você pode customizá-lo.
@Test
public 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
O OpenEJB caça sua aplicação por componentes JavaEE que podem ser de sua
responsabilidade.
www.4linux.com.br 25 / 44
26. Configurando Recursos
@Stateless
public class OrderProcessor { Configurando recursos como
@Resource DataSource, filas, tópicos,
DataSource dataSource; fábricas de conexões...
...
}
public class OrderProcessorIT {
@Test
public 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”);
}
}
www.4linux.com.br 26 / 44
27. Ligando 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;
@Resource
private Queue filaJogadores; Acesso a todos os recursos
do Container, como filas,
@EJB datasources, ejbs e ao
GameController controller; contexto cdi
…
public SetUp() throws Exception {
EJBContainer container = EJBContainer.createEJBContainer();
container.getContext().bind(“inject”, this);
}
}
www.4linux.com.br 27 / 44
28. Mais eu quero testar em um
servidor de aplicação
completo, inútil!
www.4linux.com.br 28 / 44
29. Testando na real com
JBoss 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.
www.4linux.com.br 29 / 44
30. Arquillian + 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>
Importando dependências
<scope>import</scope>
do Arquillian
<type>pom</type>
</dependency> Integração com
Junit ou TestNG
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<version>1.0.0.Final</version>
</dependency>
Falta a extensão do container...
www.4linux.com.br 30 / 44
32. JBoss 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.
www.4linux.com.br 32 / 44
33. Teste com CDI usando
Arquillian
@RunWith(Arquillian.class)
public class GerenciadorReservasTest {
@Inject
private GerenciadorReservas reservas;
@Deployment
public static JavaArchive gerandoArquivoDeploy() {
JavaArchive arquivo = ShrinkWrap.create(JavaArchive.class);
arquivo.addPackages(true, “fourlinux.justjava”);
arquivo.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
return arquivo;
}
@Test
public 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()));
}
}
www.4linux.com.br 33 / 44
34. Testando em Containers
Remotos
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-arquillian-container-remote</artifactId>
<version>7.1.1.Final</version>
<scope>test</scope>
</dependency>
Cliente JBoss Remoto
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-controller-client</artifactId>
<version>7.1.1.Final</version>
<scope>test</scope>
</dependency>
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.
www.4linux.com.br 34 / 44
35. Arquillian como Cliente
Teste OUT Container
@RunWith(Arquillian.class)
public class GerenciadorReservasTest {
Deploy no container, mas testa no cliente
@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;
}
Testando UI
com Selenium
@Test
public 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());
}
}
www.4linux.com.br 35 / 44
36. Misturando In e Out Container
@RunWith(Arquillian.class)
public class GerenciadorReservasTest {
@Inject
private GerenciadorReservas reservas;
@Deployment
public static JavaArchive gerandoArquivoDeploy() {
JavaArchive arquivo = ShrinkWrap.create(JavaArchive.class);
arquivo.addClasses(...);
...
}
Teste será executado na JVM do container
@Test
public void reservaDeveGerarCobranca() throws Exception {
….
Cobranca cobranca = reservas.reservarCabine(new Cabine[]{cabine}, usuario);
assertEquals(new Float(340), new Float(cobranca.getValor()));
}
@Test Teste será executado na JVM que inicou os testes
@RunAsClient
public void verificandoTotalItensInicial() throws Exception {
WebDriver navegador = new HtmlUnitDriver();
navegador.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
...
} www.4linux.com.br 36 / 44
37. PLUS: 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
www.4linux.com.br 37 / 44
38. Filtrando testes no Infinitest
● O Infinitest permite que você filtre os testes que serão
executados continuamente.
● Arquivo infinitest.filters
Bloqueia todos os testes de integração:
.*ITest
Bloqueia todos os testes de um pacote:
org.soujava.justjava..*
Bloqueia todos os teste do projeto:
.*
www.4linux.com.br 38 / 44
43. 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!
www.4linux.com.br 43 / 44
44. 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.
www.4linux.com.br 44 / 44