17/09/13
Tecnologias
DevMedia - Leitor Digital
Revistas
Cursos
Pocket videos
DevWare
Fórum
Serviços
Publicar
Seja bem vindo, Lineu Mialaret!
Comprar
Fale conosco
Meus Serviços
[easy Java Magazine 16 - Índice]
Go s t ei (0)
Testes Unitários com JUnit
Verificando os métodos de sua classe
De que se trata o artigo
Neste artigo aprenderemos um pouco sobre Testes de Software, sobre o que é um Teste
Unitário, veremos como utilizar o JUnit para criá-los e como implementá-los seguindo as
recomendações da Engenharia de Software. Também entenderemos um pouco sobre
Cobertura de Código para avaliarmos quão efetivos são os testes unitários criados.
Em que situação o tema é útil
O tema é útil para os desenvolvedores e times de desenvolvimento que procuram entregar
softwares de qualidade. Com os testes de software é possível avaliar se o produto de software
que se está entregando ao cliente está bem construído, se está confiável e é possível eliminar
erros do mesmo.
Resumo Devman
Neste artigo fazemos uma breve introdução ao universo dos Testes de Software, mostrando
alguns conceitos que devem acompanhar todos os envolvidos no desenvolvimento de um
produto de software. Introduzimos quais são os níveis de teste e focamos nos Testes Unitários.
Falamos um pouco sobre Cobertura de Código, métrica que nos ajuda a avaliar os testes
unitários criados, e criamos uma aplicação de exemplo a partir da qual iniciamos criando os
testes unitários utilizando o framework JUnit. Além disso, mostramos passo a passo como criar
e aperfeiçoar os testes visando uma maior efetividade dos mesmos, com o objetivo final de
entregar uma aplicação um pouco mais robusta, ou seja, que não quebra facilmente devido a
erros de programação.
O ser humano é suscetível a erros. Quando programamos, procuramos sempre desenvolver
uma lógica que implemente o que é requisitado por alguém ou que está especificado em algum
documento. Muitas vezes, sem perceber, cometemos pequenos deslizes ao programar, mas
continuamos acreditando que nosso código está coerente e consistente. Para verificar,
rodamos a nossa aplicação e observamos seu comportamento e suas respostas aos nossos
cliques e dados que digitamos. Não ocorreu nenhum erro? Então está perfeito. Será mesmo?
E se testarmos com outros dados? Ao inserirmos dados inválidos propositalmente, qual seria o
resultado? O que aconteceria se um laço while fosse executado mais vezes do que o
esperado? No caso de nosso método possuir um if/else, nosso dado de teste fez o if ser
executado? O else foi testado em algum momento?
Olhamos de novo, mais 10 vezes, e não conseguimos ver erro nenhum. Estamos certos de
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
1/18
17/09/13
DevMedia - Leitor Digital
que o código está correto. Pelo menos, pressupomos que esteja. Para ampliarmos nossa
certeza, vamos conhecer um pouco sobre Testes de Software e aprender algumas técnicas
que nos mostram que, apenas com concentração e boa intenção, não conseguimos enxergar
os problemas que estão presentes.
O que é Teste de Software?
Durante o processo de desenvolvimento, há os momentos de verificar se o software que
estamos construindo está correto e se o que estamos desenvolvendo é o que o cliente ou o
usuário realmente quer. Estes momentos de verificação fazem parte de um processo chamado
Verificação e Validação, também referenciado como V & V, e devem ocorrer em várias fases
da construção do software. Validação é o processo que visa garantir que se está construindo
o programa que o cliente de fato deseja utilizar, com as funcionalidades que ele espera que
estejam disponíveis. Ou seja, conseguimos compreender o que o cliente quer e o entregamos
conforme acertado. Verificação é o processo de garantir que o produto de software está sendo
bem construído, executando corretamente, mostrando as mensagens corretas, sem ocorrer
falhas durante sua operação. Assim esperamos.
Teste de Software é parte integrante de V & V e corresponde ao processo que consiste de
todas as atividades do ciclo de vida referentes à avaliação do software, com o objetivo de se
determinar três coisas: (1) que o mesmo satisfaz aos requisitos especificados; (2) que o
mesmo está apto para ser liberado para o cliente; (3) e para encontrar defeitos, motivo que
todos pensam ser o único.
Portanto, testar um programa não é apenas procurar erros, é um processo bem mais amplo,
com seus papéis, atividades e artefatos resultantes.
Um Caso de Teste, que nós chamamos apenas de Teste, consiste de:
• Um conjunto de valores de entrada para o sistema;
• As ações a serem executadas pelo testador ou outro programa que interaja com o software
que está sendo testado;
• Os resultados esperados como consequências das ações executadas; e
• As pós-condições, que são o resultado final gerado como consequência do que foi executado
ao longo do teste.
Para conhecer os vários tipos de teste e entender em que momento eles se aplicam, veja a
Figura 1, conhecida como Modelo V, que mostra como as atividades de teste podem ser
integradas em cada fase do ciclo de vida de desenvolvimento. Quatro tipos diferentes são
mostrados, onde cada um deles é aplicado em um nível diferente dos artefatos da aplicação,
desde um simples método até o sistema inteiro. Por isso eles definem os Níveis de Teste.
Níveis de Teste - entendendo melhor
Vamos entender estes “níveis” melhor.
Após a codificação do sistema, criação das classes, definição de seus atributos e
implementação de seus métodos, iniciamos o desenvolvimento dos Testes Unitários, que
consiste em checar se os métodos de uma classe funcionam corretamente (este é o foco deste
artigo).
Depois que todos os métodos que interessam foram testados separadamente, inicia-se os
Testes de Integração, onde se procura verificar a comunicação entre as classes. Por exemplo,
procura-se verificar os resultados dos métodos de uma determinada classe quando chamados
por métodos de outras. Nesta fase, vai-se integrando do nível mais granulado, de classe em
classe, indo até componentes inteiros interagindo com outros, mas sempre observando as
estruturas internas que estão sendo testadas nos códigos, suas condições e seus laços. Estes
testes, os unitários e os de integração, são também chamados de “Testes de Caixa Branca”,
porque estamos olhando o que há dentro do código-fonte para poder testá-los.
Após a integração, se junta todos os componentes e demais recursos que compõem o
sistema e começam-se os Testes de Sistema. Neste nível não se checa mais o código, mas se
a aplicação está fazendo corretamente o que está especificado nos requisitos, testando-a
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
2/18
17/09/13
DevMedia - Leitor Digital
através das interfaces gráficas, checando relatórios e demais artefatos gerados, simulando
mais ou menos o que um usuário faria ao usar o sistema.
Por último, os Testes de Aceitação são realizados, geralmente, pelos próprios clientes ou
usuários, onde se procura verificar se as necessidades deles foram adequadamente atendidas
e se o sistema oferece o que eles esperam.
Se estiver OK, o sistema é aceito e vai para produção. Tanto os testes de sistema quanto os
de aceitação, diferentemente dos dois primeiros, são chamados de “Testes de Caixa Preta”,
pois não se sabe como o código está estruturado, não se tem a visão de dentro da caixa.
Testa-se sem se preocupar com isso, pois o que interessa agora é a funcionalidade de alto
nível, na visão do usuário ou cliente.
Nota
Muitas vezes ouvimos falar em versão beta de algum aplicativo. Isto nada mais é do que
uma versão disponibilizada para um Teste de Aceitação a ser realizado por uma grande
quantidade de usuários finais em um ambiente de uso real. As empresas procedem assim
porque, muitas vezes, é impossível ao time de desenvolvimento ou de testes prever todas
as situações de uso de uma ferramenta na prática.
[abrir im age m e m jane la]
Figura 1. Modelo V.
Aprofundando em Testes de Unidade ou Unitários
Testes de Unidade são testes que verificam a menor unidade de projeto do software que, em
um sistema orientado a objetos, é o método. Sua meta é encontrar defeitos. Falando de forma
mais prática, testes unitários são nada mais do que outra classe Java que você cria para testar
seu código. Esta classe deve possuir métodos nos quais você instancia a classe que deseja
testar, chama seus métodos passando os parâmetros necessários, recupera seu resultado e o
avalia comparando com o que você esperava que fosse retornado. Veremos mais a frente
como implementá-los.
É importante criar testes que “exercitem” todos os métodos de uma classe e cada estrutura
que há dentro de cada um deles, ou seja, ifs, elses, for, forEach, entre outras. “Exercitar” é um
termo que significa executar um trecho de código com um teste. Portanto, é importante que
criemos testes que exercitem todas as partes do código.
Quando criamos testes unitários que atinjam a condição de exercitar muitas partes do código,
dizemos que atingimos uma boa “cobertura de código”.
“Cobertura” é uma indicação de quais linhas, e quais caminhos diferentes dentro dos métodos,
conseguimos verificar nos nossos testes.
Veja o tópico “Cobertura de Código” para entender melhor como se consegue uma boa
cobertura verificando linhas e caminhos.
Apenas para ilustrar, observe o código da Listagem 1. Vemos que há um método calcular()
que possui um while que, por sua vez, possui um if/else. É importante observar que, ao
criarmos testes unitários para este exemplo, devemos exercitar o laço while, o if e o else.
Quantos testes unitários seriam necessários para isso?
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
3/18
17/09/13
DevMedia - Leitor Digital
Para exercitar o while, precisamos que qtd tenha um valor menor do que 50, caso contrário,
não ocorreria nenhuma iteração. Se o valor informado em qtd for igual a 10, exercitaríamos o
while e o if. Teríamos, assim, um teste passando 10 para qtd. Se criássemos outro teste onde
o valor passado fosse menor que 50, não sendo 10, teríamos exercitado o while e o else.
Concluindo, precisaríamos de dois testes unitários para atingir uma boa cobertura do código
da Listagem 1.
Usando TDD - Test Driven Developme
Nota
nt ou Desenvolvimento Guiado Por Testes - o desenvolvedor, antes mesmo de começar a
programar, inicia com a criação de um teste unitário e, só depois, implementa o método.
Afinal, o teste é que guia o desenvolvimento. Se você vai iniciar um projeto ou alguma
nova funcionalidade, pesquise esta técnica.
Listagem 1. Exemplo de método a ser exercitado.
public void calcular (int qtd) {
while (qtd < 50) {
if (qtd == 10) {
// realiza alguma operacao.
} else {
// senão, realiza outra operacao.
}
}
}
Cobertura de Código
Cobertura de Código é um método de análise que permite verificar
quão efetivo foram os testes unitários. Com este método é possível
ter a noção de quais partes do software foram, ou não, executadas
(ou cobertas) pela suite de testes. Lembrando que quando
executadas, dizemos que tais partes foram “exercitadas”.
Para avaliar a cobertura de código, criaram-se várias métricas que
realizam diversas verificações no código testado. Dentre as mais
utilizadas estão: cobertura de comandos (statement coverage) e a
cobertura de desvios (branch coverage). Vamos entendê-las nos
parágrafos seguintes.
A Cobertura de Comandos (statement coverage), também
conhecida como cobertura de linha, mede que linhas foram
exercitadas pelos testes unitários.
Por exemplo, no código a seguir, é possível notar que se criarmos
um único teste unitário passando para o methodA() os valores 5 e
4, apenas a linha do if, que soma ‘a´ + ‘b´, e a linha que retorna ‘c´
seriam exercitadas.
Neste caso, não conseguiríamos exercitar todos os comandos do
nosso método. Portanto, a cobertura de linhas do método não seria
de 100%.
public int methodA(int a, int b) {
if(a > b) {
c = a + b;
}
else {
c = a - b;
}
return c;
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
4/18
17/09/13
DevMedia - Leitor Digital
}
Neste caso, cobrimos apenas um desvio, o do if. Portanto,
também não temos 100% de cobertura de desvios. Para
conseguirmos cobrir 100% das linhas deste código e cobrir todos
os desvios, será necessário criar mais um teste unitário passando
qualquer valor onde ‘b´ é maior que ‘a´, podendo ser 5 e 4, por
exemplo. Com isso, conseguiríamos exercitar o else. Olhando a
cobertura dos dois testes separadamente, nenhum deles consegue
alcançar os 100%. Mas uma suite de testes, que é um conjunto
destes, com os dois juntos atingem os objetivos, tanto o da
cobertura de comandos, ou linhas, quanto o da cobertura de
desvios.
Assim, é fácil identificar que precisaríamos de dois
testes unitários, mas em uma situação mais complexa,
no dia a dia, não seria tão evidente. Por este motivo o
uso destas técnicas faz a diferença.
Criando nossa aplicação de
exemplo
Para ilustrar o uso de testes unitários com o JUnit,
vamos criar uma aplicação de exemplo bem simples,
mas que contém os elementos que são importantes de
destacar na hora de testar as unidades de um sistema.
Para isso, utilizaremos a IDE Eclipse Indigo.
A nossa aplicação é responsável por fazer um
controle de empréstimo de livros. Para isso,
precisamos de uma entidade Livro para poder iniciar
os trabalhos. A Listagem 2 mostra como seria a nossa
classe. A mesma tem os atributos autor, titulo,
emprestado e reservado. Estes dois últimos guardam o
status em relação a sua disponibilidade, está
emprestado (true) ou não (false) e reservado (true) ou
não (false). Também possui um atributo chamado
historico que é do tipo lista de Emprestimos, cuja
classe pode ser vista na Listagem 3.
Cada empréstimo da lista contém informações como
quem foi o Usuario (Listagem 4) que pegou o livro
emprestado, qual foi a data do empréstimo
(dataEmprestimo), e qual foi a data da devolução
(dataDevolucao), ambos do tipo Date, e a lista de livros
locados pelo usuário.
Livro possui o método emprestar(), que simplesmente
verifica se o atributo emprestado está como false,
antes de emprestá-lo setando true. Ao fazê-lo, também
imprime uma mensagem no console do
Eclipse/NetBeans informando que o livro foi
emprestado com sucesso. Esta classe também possui
o método consultarEmprestimosPorUsuario(), para o
qual é passada uma instância da classe Usuario, na
Listagem 4. Esta nossa aplicação de brinquedo tem o
objetivo apenas de criar elementos com as estruturas
necessárias para exemplificar o uso de testes unitários
com o JUnit.
Listagem 2. Classe Livro da nossa aplicação fictícia
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
5/18
17/09/13
DevMedia - Leitor Digital
de controle de empréstimos.
public class Livro {
private String autor;
private String titulo;
private boolean emprestado;
private boolean reservado;
private List<Emprestimo> historico;
public boolean emprestar(){
if(!this.isEmprestado()){
this.setEmprestado(true);
System.out.println("Livro emprestado com sucesso.");
}
return this.isEmprestado();
}
public List<Emprestimo> consultarEmprestimosPorUsuario(Usuario usuario)
{
List<Emprestimo> emprestimosPorUsuario = new ArrayList<Emprestimo>
();
for (Emprestimo emprestimoUsuario : emprestimosPorUsuario) {
if(emprestimoUsuario.getUsuario().equals(usuario))
{
emprestimosPorUsuario.add(emprestimoUsuario);
}
}
return emprestimosPorUsuario;
}
}
Listagem 3. Classe Emprestimo da nossa aplicação
fictícia de controle de empréstimos.
public class Emprestimo {
private Usuario usuario;
private Date dataEmprestimo;
private Date dataDevolucao;
private List<Livro> livros;
}
Listagem 4. Classe Usuario com método equals().
public class Usuario {
private String nome;
private String matricula;
private boolean emDebito;
public Usuario(String nome, boolean emDebito) {
super();
this.nome = nome;
this.emDebito = emDebito;
}
@Override
public boolean equals(Object obj){
if(obj instanceof Usuario){
Usuario outro = (Usuario) obj;
return outro.getNome().equals(getNome());
} else {
return false;
}
}
}
No método consultarEmprestimosPorUsuario() é
necessário fazer uma comparação para saber se o
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
6/18
17/09/13
DevMedia - Leitor Digital
usuário passado como parâmetro tem o mesmo nome
do usuário que fez o empréstimo, portanto a classe
Usuario sobrescreve o método equals(). Para
comparações utilizando este método, é necessário
sobrescrevê-lo, o implementando da forma que for
desejada; no caso, comparando pelo atributo nome. Se
fizéssemos a mesma comparação sem sobrescrevê-lo,
duas instâncias de Usuario só seriam iguais se
referenciassem exatamente o mesmo objeto, pois Java
compararia a identidade do objeto, que é diferente
para cada instância presente na JVM.
Exposta a estrutura da nossa aplicação, que contém
alguns erros que passaram despercebidos, vamos
instalar o JUnit para que possamos verificar nossos
métodos.
Instalando o JUnit
O JUnit é um framework de testes unitários
desenvolvido em Java por iniciativa de Kent Beck e
Erich Gamma. Ele já vem integrado nas IDEs mais
atuais. No momento de criar seu projeto, basta
adicioná-lo ao Java Build Path clicando no seu projeto
e selecionando o menu File | Properties > Java Build
Path, selecionando a aba Libraries e pressionando o
botão Add Library, como mostra a Figura 2.
Na tela Add Library, selecione a opção JUnit e
pressione o botão Next. Então, mude a opção JUnit
library version para JUnit 4, como mostra a Figura 3, e
clique em Finish. A versão que vem pré-instalada no
Eclipse Indigo é a versão 4.8.2.
Outra opção seria acessar o site do JUnit e baixar a
última versão (no momento da escrita deste artigo é a
4.10). Baixe o arquivo junit-<versao>.jar, crie uma
pasta lib na raiz do seu projeto, se já não houver.
Adicione seu arquivo jar nesta pasta. Faça um refresh
do seu projeto para visualizar a pasta e o arquivo.
Clique com o botão direito do mouse sobre o arquivo e
selecione a opção Build Path > Add to Build Path. Ao
concluir estes passos, seu projeto já estará apto para
utilizar os recursos disponibilizados pelo JUnit para
criação de testes unitários.
[abrir im age m e m jane la]
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
7/18
17/09/13
DevMedia - Leitor Digital
Figura 2. Adicionando o JUnit ao build path.
[abrir im age m e m jane la]
Figura 3. Alterando a versão do JUnit.
Criando os testes unitários de
nossa aplicação
O nosso objetivo no momento é conseguir aplicar a
teoria que vimos até então no software de exemplo que
criamos no tópico anterior. Ao utilizar as técnicas
apresentadas, conseguiremos criar bons testes
unitários e, por consequência, obteremos uma boa
“cobertura” com os mesmos.
Antes de qualquer coisa, é uma prática criar os testes
unitários em um pacote à parte, geralmente chamada
test, dentro da pasta src.
Procure criar os pacotes de teste com caminhos
similares aos das classes testadas. Por exemplo, para
as classes do pacote biblioteca.entidade, crie classes
de teste em um pacote biblioteca.entidade.test. No final
das contas, a estrutura de pastas de nosso projeto
ficaria como indicado na Figura 4.
[abrir im age m e m jane la]
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
8/18
17/09/13
DevMedia - Leitor Digital
Figura 4. Estrutura de pastas do projeto.
Geralmente, cria-se uma classe de teste para cada
classe a ser testada do projeto. Para criar testes
unitários para a nossa aplicação com o JUnit, é
necessário selecionar uma classe.
Assim, vamos utilizar Livro. Pressione o botão direito
do mouse em cima dela. Ao abrir o menu, selecione
New > JUnit Test Case. Uma tela como a da Figura 5 é
exibida. Informe o nome que desejar para a classe de
teste. O default é: <o nome da classe a ser
testada>+“Test”, no nosso caso, LivroTest.
[abrir im age m e m jane la]
Figura 5. Tela de caso de teste.
Na Figura 5, onde vemos o campo Package, indique o
pacote de testes. No campo Name, você pode indicar o
nome que quiser, possuindo ou não o sufixo “Test”. O
mais interessante de observar é o conjunto de caixas
de checagem que tem mais abaixo na Figura 5. Dentre
elas, setUp() já vem marcada. Deixe assim - veremos
mais adiante do que se trata, e clique em Next >.
A próxima tela pode ser observada na Figura 6. Nela
é possível ver a classe selecionada e os seus
métodos. Ao selecioná-los, uma classe de teste será
criada com testes apenas para os métodos que foram
selecionados. Não é necessário criar testes para os
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
9/18
17/09/13
DevMedia - Leitor Digital
gets e sets, a não ser que eles contenham alguma
regra de negócio. Deste modo, marque os métodos
emprestar() e consultarEmprestimosPorUsuario() e
clique em Finish.
[abrir im age m e m jane la]
Figura 6. Selecionando os métodos a se testar.
O resultado é o mostrado na Listagem 5. Observe
que há três métodos: setUp(), testEmprestar() e
testConsultarEmprestimosPorUsuario(). O primeiro foi
criado automaticamente porque a caixa de checagem
de mesmo nome, exibida na Figura 5, estava marcada.
Este método está anotado com @Before, o que
determina que será executado antes de qualquer
método de teste desta classe. Nele, é possível criar
qualquer configuração inicial que será utilizada pelos
demais testes da classe, como instanciar e atribuir
elementos a uma coleção, instanciar demais objetos
necessários, conectar com algum recurso, entre outras
coisas. Os dois métodos seguintes são os da classe
Livro que selecionamos para teste, como indica a
Figura 6, acrescidos da palavra “test” como prefixo,
que não é obrigatório. Eles são anotados com @Test,
o que determina para o JUnit quem são os métodos de
teste.
Listagem 5. Código da classe LivroTest.
public class LivroTest {
@Before
public void setUp() throws Exception {}
@Test
public final void testEmprestar() {
fail("Not yet implemented"); // TODO
}
@Test
public final void testConsultarEmprestimosPorUsuario() {
fail("Not yet implemented"); // TODO
}
}
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
10/18
17/09/13
DevMedia - Leitor Digital
Para executar esta classe de teste recém-criada,
basta clicar com o botão direito em cima da mesma, no
Project Explorer, e selecionar Run As > JUnit Test.
O JUnit abrirá uma view como a mostrada na Figura 7,
onde é possível ver o tempo gasto na execução dos
testes, quantos foram rodados do total (Runs: 2/2),
quantos erros ocorreram (Errors: 0) e quantas falhas
(Failures: 2). Um erro indica que ocorreu algum
problema e o teste não foi concluído. Uma falha indica
que o teste foi concluído e o resultado esperado não
foi o desejado, portanto, há algum defeito na classe
sendo testada.
Ao clicarmos no método testEmprestar() na view do
JUnit, por exemplo, é possível ver qual a razão do
mesmo ter falhado observando a Failure Trace. Neste
ponto, criamos uma classe de testes unitários e os
executamos. O que nos falta agora é colocar a lógica
de teste nestes métodos, instanciando as classes de
nossa aplicação dentro deles, chamando seus
métodos e verificando os resultados.
[abrir im age m e m jane la]
Figura 7. Resultado da execução inicial da classe
LivroTest.
Implementando nosso teste
Após termos aprendido como criar e executar um
teste unitário, vamos implementar a nossa classe de
teste, LivroTest. Para começar, crie um membro Livro
nela. Vamos instanciá-lo no método setUp(). Configure
seus atributos da seguinte forma: o autor será “Jorge
Amado”, o titulo será “Capitães da Areia” e
determinaremos que o atributo reservado, passado no
construtor, será false. Além disso, crie três instâncias
de Emprestimo, determine o Usuario de cada uma
delas, como mostrado na Listagem 6, e os adicione ao
atributo historico, do objeto livro. Observe que o
usuário “José” se repete, pois seria seu segundo
empréstimo deste livro. Desta forma temos um livro de
Jorge Amado que foi locado três vezes, duas das quais
por José. O segundo parâmetro do construtor de
Usuario é o status que indica se o mesmo está em
débito ou não. Na terceira locação, representado pelo
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
11/18
17/09/13
DevMedia - Leitor Digital
objeto emprestimo3, José estava com débitos na
biblioteca. Este é o contexto no qual estamos
configurando nosso teste da classe Livro.
Listagem 6. Método setup() da classe LivroTest.
public class LivroTest {
Livro livro;
@Before
public void setUp() {
livro = new Livro("Jorge Amado", "Capitães da Areia", false);
Emprestimo emprestimo1 = new Emprestimo();
emprestimo1.setUsuario(new Usuario("Jose", false));
Emprestimo emprestimo2 = new Emprestimo();
emprestimo2.setUsuario(new Usuario("João", false));
Emprestimo emprestimo3 = new Emprestimo();
emprestimo3.setUsuario(new Usuario("Jose", true));
livro.getHistorico().add(emprestimo1);
livro.getHistorico().add(emprestimo2);
livro.getHistorico().add(emprestimo3);
}
// demais métodos omitidos.
}
Para cada “objeto de teste”, no caso cada método da
classe Livro, é interessante realizar um “teste positivo”,
no qual testamos uma situação de sucesso, e um
“teste negativo”, no qual verificamos se nossa
aplicação trata corretamente uma situação incorreta. A
partir de agora, incrementaremos um pouco mais o
nosso teste. Observando a classe Livro, na Listagem
2, o método emprestar() apenas verifica se o livro não
está emprestado para que ele possa atribuir true ao
atributo emprestado, ou seja, emprestá-lo. Caso esteja
emprestado, não faz nada e retorna a situação atual
do livro. Portanto, precisamos fazer dois testes: um
positivo, no qual o livro não está emprestado e
chamamos o método emprestar(); e outro negativo, no
qual o livro já está emprestado e chamamos o método
emprestar() mesmo assim.
Observando os objetivos de cobertura que vimos
antes, neste método temos uma estrutura if, portanto é
importante criar duas situações: uma onde a condição
lógica seja verdadeira, para entrar no mesmo; e outra
onde a condição seja falsa, para não entrar e não
realizar qualquer operação, para assim podermos
avaliar o comportamento completo do método.
É importante verificar as duas situações de uma
condição lógica para podermos avaliar o que acontece
com os objetos e variáveis manipulados pelo método
caso as operações que deveriam ser realizadas dentro
do if não se realizem.
Alguns erros podem estar contidos aí. Com este fim,
renomeamos o método da classe de teste
testEmprestar() para
testEmprestarLivroNaoReservado() e criamos o
testEmprestarLivroReservado() que corresponde ao
“teste negativo” de empréstimo de um livro. Estes dois
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
12/18
17/09/13
DevMedia - Leitor Digital
métodos ficariam como na Listagem 7.
Com o JUnit, a forma de verificarmos se o resultado
da classe a ser testada é o esperado ou não é
utilizando um conjunto de assertivas: assertTrue(),
assertFalse(), assertEquals(), assertNull(),
assertNotNull(), só para citar algumas. Portanto, se
esperamos um valor lógico true como resultado de um
método, utilizamos o assertTrue() passando como
parâmetro o retorno do método. Se desejamos verificar
que dois objetos são iguais, um retornado por um
método e outro que você criou no corpo do método de
teste unitário, utilizamos um assertEquals() passando
os dois objetos como primeiro e segundo parâmetros.
Listagem 7. Métodos da classe LivroTest.
public class LivroTest {
Livro livro;
@Before
public void setUp() {
livro = new Livro("Jorge Amado", "Capitães da Areia", false);
Emprestimo emprestimo1 = new Emprestimo();
emprestimo1.setUsuario(new Usuario("Jose", false));
Emprestimo emprestimo2 = new Emprestimo();
emprestimo2.setUsuario(new Usuario("João", false));
Emprestimo emprestimo3 = new Emprestimo();
emprestimo3.setUsuario(new Usuario("Jose", true));
livro.getHistorico().add(emprestimo1);
livro.getHistorico().add(emprestimo2);
livro.getHistorico().add(emprestimo3);
}
@Test
public void testEmprestarLivroNaoReservado(){
assertTrue(livro.emprestar());
}
@Test
public void testEmprestarLivroReservado(){
livro.setReservado(true);
assertFalse(livro.emprestar());
}
// demais métodos omitidos.
}
Com a classe de teste mais completa, podemos
executá-la novamente.
Ao executar, é possível verificar que o teste
testEmprestarLivroReservado() falha. Por que será?
Se você observar, o nosso teste está correto, pois não
podemos emprestar um livro que está reservado. Esta
falha nos mostra um erro de lógica na nossa aplicação.
Se um estudante, ou usuário da biblioteca, pegar um
exemplar na estante e levar até a bibliotecária para
pegá-lo emprestado, ela não precisa checar se o livro
está emprestado ou não, visto que o mesmo estava
disponível no acervo. Para os operadores do nosso
sistema, isto não faz sentido: ou o livro está disponível
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
13/18
17/09/13
DevMedia - Leitor Digital
nas dependências da biblioteca, ou já está emprestado
para outro usuário. A implementação mais adequada
do método emprestar(), da nossa classe Livro, seria
como o mostrado na Listagem 8. Quanto à reserva,
devemos verificar se o livro está ou não reservado e,
por algum motivo, não estava separado dos demais, no
setor de reservas. Caso esteja reservado, não se pode
emprestá-lo. Caso contrário, o usuário poderá ter uma
boa leitura no conforto do seu lar. O nosso teste nos
mostrou uma falha negocial ao refletirmos sobre a
lógica do método a ser testado. Ao executarmos os
testes novamente, podemos verificar que agora ele
passou. O ícone ao lado do nome do teste, na view do
JUnit, se torna uma checagem verde ( ).
Listagem 8. Nova implementação do método
emprestar() da classe Livro.
public boolean emprestar(){
if(this.isReservado()){
this.setEmprestado(false);
System.out.println("Livro está reservado.");
} else {
this.setEmprestado(true);
System.out.println("Livro emprestado com sucesso.");
}
return this.isEmprestado();
}
Vamos agora implementar os testes do método
consultarEmprestimosPorUsuario(). Este método
possui uma lista dos empréstimos do livro e itera sobre
ela buscando se o usuário é o mesmo que o passado
por parâmetro, como mostrado na Listagem 2. Quando
há laços, uma recomendação mais rigorosa nos exige
criar pelo menos três testes: um teste onde
executamos o laço pelo menos uma vez; outro onde
não executamos nenhuma; e outro quando executamos
mais de duas vezes. Porque não executar nenhuma?
Às vezes, nós contamos que sempre ocorrerá várias
iterações de um laço e implementamos o método
contando com isso, definindo um conjunto de
operações dentro do mesmo. Quando o laço não é
executado nenhuma vez, e não contávamos com isso,
alguma operação óbvia deixou de ser executada,
comprometendo assim toda a função do método e,
consequentemente, prejudicando quem aguardava o
retorno do mesmo. A nossa classe de teste ficaria
como mostra a Listagem 9.
Listagem 9. Código da classe LivroTest.
public class LivroTest {
Livro livro;
@Before
public void setUp() {
livro = new Livro("Jorge Amado", "Capitães da Areia", false);
Emprestimo emprestimo1 = new Emprestimo();
emprestimo1.setUsuario(new Usuario("Jose", false));
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
14/18
17/09/13
DevMedia - Leitor Digital
Emprestimo emprestimo2 = new Emprestimo();
emprestimo2.setUsuario(new Usuario("João", false));
Emprestimo emprestimo3 = new Emprestimo();
emprestimo3.setUsuario(new Usuario("Jose", true));
livro.getHistorico().add(emprestimo1);
livro.getHistorico().add(emprestimo2);
livro.getHistorico().add(emprestimo3);
}
@Test
public void testEmprestarLivroNaoReservado(){
assertTrue(livro.emprestar());
}
@Test
public void testEmprestarLivroReservado(){
livro.setReservado(true);
assertFalse(livro.emprestar());
}
@Test
public void testNenhumEmprestimoPorUsuario(){
Usuario novoUsuario = new Usuario("Judas", false);
List<Emprestimo> historico = livro.consultarEmprestimosPorUsuario(novoUsuario);
int resultadoReal = historico.size();
assertEquals(0, resultadoReal);
}
@Test
public void testUmEmprestimoPorUsuario(){
Usuario novoUsuario = new Usuario("João", false);
List<Emprestimo> historico = livro.consultarEmprestimosPorUsuario(novoUsuario);
int resultadoReal = historico.size();
assertEquals(1, resultadoReal);
}
@Test
public void testDoisEmprestimosPorUsuario(){
Usuario novoUsuario = new Usuario("Jose", false);
List<Emprestimo> historico = livro.consultarEmprestimosPorUsuario(novoUsuario);
int resultadoReal = historico.size();
assertEquals(2, resultadoReal);
}
}
Ao executarmos nossos testes agora, vemos que
quase todos passaram, com exceção de
testUmEmprestimoPorUsuario() e
testDoisEmprestimosPorUsuario(). Quando analisamos
o método consultarEmprestimosPorUsuario() da classe
Livro, verificamos que há um erro muito elementar:
esquecemos que o método itera por uma coleção que
foi criada dentro dele mesmo. Como poderia ter
alguma coisa se acabamos de criá-la? A correção
deste problema consiste apenas em alterar a instrução
for para que itere na coleção historico, membro da
classe Livro, e não na coleção emprestimosPorUsuario,
que é interna ao método e é, na verdade, seu objeto
de retorno. Após esta correção, todos os testes
passam com sucesso, como mostra a Figura 8.
[abrir im age m e m jane la]
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
15/18
17/09/13
DevMedia - Leitor Digital
Figura 8. Resultado dos testes com sucesso.
Parece óbvio, mas qual o benefício mesmo?
Realmente parece óbvio, pois quando estamos
criando testes unitários, estamos criando métodos para
testar outros métodos. E ao fazer isto, também
estamos corrigindo nossa aplicação. Ao criá-los,
devemos fazê-lo buscando verificar cada estrutura de
cada método, para a maior quantidade de métodos
possível de nossa aplicação.
Tendo esta coleção de testes em mãos, toda vez que
for necessário fazer alguma alteração nas regras de
negócio ou corrigir algum erro encontrado por um
usuário ou até mesmo uma melhoria, altere
tranquilamente. Após fazer o que é necessário, basta
reexecutar o seu arsenal de testes já pronto para
verificar se o que você alterou, corrigiu ou melhorou,
não danificou alguma parte do sistema que já estava
funcionando.
A consequência negativa que pequenas alterações
podem acarretar ao sistema também é conhecida por
“efeito gelatina”; como se sua aplicação fosse uma
gelatina num pires sendo arrastado sobre uma mesa
que, ao mexer, tudo treme. Analogamente ao
desenvolvimento de sistemas, se você fizer uma
pequena alteração, esta poderia causar algum tremor
no seu sistema, pois, sem querer, poderia ter
consequências em outras partes que se relacionam
com a parte alterada.
Os testes unitários evitam este efeito porque antes de
liberar um build do sistema, você reexecutaria os testes
unitários. Desta forma você se sentirá bem mais
seguro ao mexer em um código que já existe e/ou que
foi criado por outro programador.
Conclusões
Neste artigo, aprendemos sobre o que é Teste de
Software, qual o seu papel no processo de
desenvolvimento de software e quais os Níveis de
Teste que são definidos nesta área específica da
Engenharia de Software. Aprofundamos no nível
específico dos Testes de Unidade (ou unitários), sua
definição e seu papel dentro dos testes em geral.
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
16/18
17/09/13
DevMedia - Leitor Digital
Aprendemos também a criar testes unitários utilizando
um framework bastante conhecido, o JUnit, que,
enquanto este artigo estava sendo escrito, sua versão
mais recente era a 4.10. Ao longo do artigo, criamos
uma aplicação de brinquedo apenas para ilustrar
situações corriqueiras e importantes nas quais os
testes de unidade podem ajudar bastante no processo
de codificação de um sistema.
Falamos superficialmente sobre um método de análise
que nos permite avaliar quão efetivo estão os nossos
testes unitários, método este que nos auxilia na
necessidade de identificar se precisamos criar mais
testes ou não, que são as métricas de cobertura de
código.
Assim, esperamos ter demonstrado de forma clara e
didática para quê servem os testes unitários, como
criá-los da melhor forma e qual a sua grande utilidade
ao longo do desenvolvimento e manutenção de um
sistema.
João dos Prazeres Farias
Mestre em Ciência da Computação com ênfase em automação de
testes pela UFPE, bacharel em Ciência da Computação pela UNICAP.
Professor do Instituto Superior Fátima, em Brasília, ministrando as
disciplinas de Lógica de Programação e Projeto I e II. É desenvolvedor
desde 1999 e trabalha com Java desde 2006.
eduardo v inicius ferreira de
oliv eira
31/3/2012 14:20
Boa tarde, estou tentando fazer os testes com o JUnit seguindo os passos do artigo mas
estou com o seguinte erro:
Could not connect to: : 49382
java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:69)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:337)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:198)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:180)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:157)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:391)
at java.net.Socket.connect(Socket.java:579)
at java.net.Socket.connect(Socket.java:528)
at java.net.Socket.(Socket.java:425)
at java.net.Socket.(Socket.java:208)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.connect(RemoteTestRunner.java:570)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:381)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
17/18
17/09/13
DevMedia - Leitor Digital
O que devo fazer ?
desde ja agradeço
Responder
Olá Eduardo, solicitei um dos nossos consultores a te ajudar, peço que aguarde um
pouco.
WESLEY YA MA ZA CK
4/2/2012 10:14:47 PM
Obrigado e um abraço
Responder
Chegou a carregar o Bacno de DAdos/Servidor de Aplicação ?
Este erro quer dizer que o servidor/banco estão baixados.
Responder
DYEGO SOUZA DO CA RMO
4/3/2012 8:44:36 A M
DevMedia
Curtir
13.551 pessoas curtiram DevMedia.
DevMedia | Anuncie | Fale conosco
Hospedagem web por Porta 80 Web Hosting
2013 - Todos os Dire itos R e se rvados a we b-03
P lug-in social do F acebook
www.devmedia.com.br/websys.5/webreader.asp?cat=61&artigo=4419&revista=easyjavamag_16#a-4419
18/18
Download

Tutorial do JUnit - Lineu FS Mialaret