quarta-feira, 23 de abril de 2008

Definição Conceitual

A atividade de teste sempre fez parte do processo de desenvolvimento de software, porém, na maioria das empresas os testes nem sempre são tratados com a devida seriedade. O que se testemunha são testes feitos muitas vezes pelo próprio analista de requisitos ou desenvolvedor, sem planejamento ou análise de cobertura, de forma estanque após o término do desenvolvimento e pouco antes da entrega do produto. Se deixar a realização dos testes apenas para o final do projeto, corre-se o risco de prejudicar a fase de teste por não se ter tempo para verificar a aplicação como planejado e resolver os problemas encontrados, por causa de atrasos durante o desenvolvimento do software.

Teste unitário é um dos fundamentos principais do desenvolvimento de software. Primeiro devemos estar consciente da utilização de um framework de testes unitários para estarmos aptos a criar suítes de testes unitários automatizadas. Segundo, devemos testar todas as classes do sistema. Os métodos getters e setters triviais normalmente são omitidos. E por último, devemos tentar criar nossos testes, antes do código.

Os testes unitários ficam disponíveis dentro do repositório de código junto o código que eles testam. Código sem teste podem não ser liberados.

A maior resistência em dedicar tempo para os testes unitários é a necessidade de rapidez para os prazos. Mas, durante a "vida" do projeto um teste automatizado pode salvar você em cem vezes o custo de criá-lo, por encontrar e prevenir bugs. O mais difícil do teste é escrever mais do que você precisa por maiores que suas economias sejam. Testes automatizados oferecem um retorno maior do que o custo da criação.

Outro conceito errado muito comum é que os testes unitários podem ser escritos nos últimos três meses do projeto. Infelizmente, sem os testes unitários o desenvolvimento fica parado e todos estes três meses são perdidos. Mesmo se o tempo está disponível, boas suítes de testes unitários levam tempo para serem desenvolvidas.

Descobrir todos os problemas que ocorrem leva tempo. Com o objetivo de ter uma suíte completa de testes unitários para quando você precisar dela, você deverá começar a criar os testes hoje enquanto você ainda não precisa.

Testes unitários fornecem código de propriedade coletiva. Quando você cria testes unitários você previne sua funcionalidade da existência de um acidente que cause danos. Exigindo que todo o código passe por todos os testes unitários antes de ser liberado, garante que toda a funcionalidade seja perfeita. A propriedade de código não é exigida se todas as classes são protegidas pelos testes unitários.

Os testes unitários também permitem o refactoring. Depois de cada pequena mudança os testes unitários podem confirmar que uma mudança na estrutura não criou uma mudança na funcionalidade.

A construção de uma suíte de teste universal simples para teste de validação permite uma integração freqüente. É possível integrar quaisquer mudanças recentes rapidamente e então rodar sua própria versão mais recente da suíte de testes. Quando um teste falha suas versões mais recentes são incompatíveis com as versões mais novas da equipe. Corrigir pequenos problemas de tantas em tantas horas leva menos tempo do que corrigir problemas grandes logo antes do prazo final (deadline). Com os teste unitários automatizados é possível unir um conjunto de mudanças com a versão mais recente que tenha sido liberada e liberar este 'merge' em pouco tempo.

Muitas vezes, a adição de novas funcionalidades irão exigir mudanças nos testes unitários para refletir a funcionalidade. Os teste unitários fornecem uma rede segura de testes e validação, então você pode refazer e integrar efetivamente

O momento de realização dos Testes unitários:

  • Quando: durante a implementação
  • Como: teste de classes e pedaços de código isolados
  • Responsável: Desenvolvedor

A nova visão da qualidade de software está voltada para prevenção de defeitos através da melhoria do processo de desenvolvimento de software. Essa nova abordagem deixa de lado a idéia de que o teste de software é mero coadjuvante no processo de desenvolvimento.

É fundamental a definição de um processo de desenvolvimento, incluindo o de teste, para que seja possível a extração de medidas das atividades realizadas e artefatos produzidos nos projetos. Somente dessa maneira será possível a obtenção dos indicadores que, juntamente com uma equipe de testes independente e capacitada, possibilitarão a análise e comparação dos resultados dos projetos da empresa.

Assim, estará garantida a melhoria do processo de desenvolvimento e, conseqüentemente, a melhoria da qualidade do produto entregue ao cliente.

Entradas Esperadas

Testar classes Java não envolve muito mais do que o desenvolvedor esta acostumado a fazer. A grande diferença entre um teste de uma situação real e um simples exemplo de teste, esta na complexidade e tamanho do código escrito. Com uma grande base de códigos, torna-se necessário agregar relatórios dos erros gerados, sem a necessidade de interromper o teste a cada momento que fosse encontrado um problema, recompilando-o. Isso tornaria o desenvolvimento de software realmente lento. Bugs são encontrados freqüentemente. Vendo todas as falhas ocorridas durante os testes em um momento, auxilia a isolar e resolver todos os problemas de uma só vez e rapidamente.

Testando uma classe Java

A seguir será apresentada uma classe para ser testada. Isso será feito através de chamadas aos seus métodos, utilizando uma outra classe Java. O código Java mostrado a seguir é um exemplo, seu conteúdo esta organizado em um arquivo chamado "InscricaoEstadual.Java", conforme mostrado:

/* 
* Created on 22/01/2004
*
* Néki Technologies
*/

package com.neki;

/**
* Validar um numero de inscricao estadual. Segue a definição aplicada pela
* Secretaria de Estado da Receita.
*
* @author Néki Team
*
*/

public class InscricaoEstadual {
public static boolean isValid(String inscricao) {
boolean bValido = false;
int iSoma = 0;
int iDigito;
if (inscricao.length()==8){
// recupera o dígito verificador
String sDigitoVerificador = inscricao.substring(inscricao.length()-1);
int iDigitoVerificador = Integer.parseInt(sDigitoVerificador);
iSoma = Integer.parseInt(String.valueOf(inscricao.charAt(0))) * 2;
iSoma = iSoma +(Integer.parseInt(String.valueOf(inscricao.charAt(1)))*7);
iSoma = iSoma +(Integer.parseInt(String.valueOf(inscricao.charAt(2)))*6);
iSoma = iSoma +(Integer.parseInt(String.valueOf(inscricao.charAt(3)))*5);
iSoma = iSoma +(Integer.parseInt(String.valueOf(inscricao.charAt(4)))*4);
iSoma = iSoma +(Integer.parseInt(String.valueOf(inscricao.charAt(5)))*3);
iSoma = iSoma +(Integer.parseInt(String.valueOf(inscricao.charAt(6)))*2);
int iResto = iSoma % 11;
if (iResto <= 1){
if (iDigitoVerificador == 0){
bValido = true;
}
}else{
if (iDigitoVerificador == (11-iResto)){
bValido = true;
}
}
}
return bValido;
}
}

A classe Java "InscricaoEstadual" oferece uma interface simples:

  • IsValid: um método para testar se a inscrição estadual informada é válida.

Para saber se um número de inscrição estadual é válido, consulte a regra para o cálculo do dígito verificador da inscrição estadual (para as inscrições do Estado do Rio de Janeiro) abaixo:


Se o resto da divisão por 11 for menor ou igual a 1 (um), então o dígito verificador será = 0 (zero). Se não, o dígito será 11 (onze) menos o resto.

Procedimentos

Para escrever um teste utilizando o JUnit, estenda a classe junit.framework.TestCase. Sua subclasses de TestCase deverá simplesmente invocar os testes na ordem que você desejar, possivelmente também executará um inicializador antes dos testes e um finalizador após os testes. O inicializador é feito através do método chamado setUp. O finalizador é feito através de um método chamado tearDown. Você deve sobrescrever ambos que deseje utilizar, mas isso não é obrigatório.

Este é um exemplo de uma classe de teste para a situação proposta anteriormente:

/* 
* Created on 22/01/2004
*
* Néki Technologies
*/
package com.neki.test;

import com.neki.InscricaoEstadual;
import junit.framework.TestCase;

/**
* @author Néki Team
*
*/

public class InscricaoEstadualTest extends TestCase {

/**
* Constructor for ValidarInscricaoEstadualTest.
* @param arg0
*/
public InscricaoEstadualTest(String arg0) {
super(arg0);
}

final public void testIsValid() {
boolean b = InscricaoEstadual.isValid("81501319");
assertTrue("Inscricao Estadual invalida",b);
}
}

Adotamos como padrão para o nome da classe Java de teste a utilização do sufixo "Test" após o nome da classe que esta sendo testada - InscricaoEstadualTest. É adotado o nome "test" como padrão para o nome do pacote onde as classes Java de teste se encontram - xxx.yyy.test.

Ao executar o JUnit todas as suas expectativas são validadas através do métodos chamados assertXxx, onde Xxx pode ser True, False Equals, ou outra condição. Além disso, pode ser passada para esses métodos alguma mensagem especifica caso o teste falhe. O JUnit registra todo o caminho percorrido durante as falhas obtidas nos método assertXxx e relata após a execução de todos os testes. Abaixo segue alguns dos métodos de assertivas que existem no JUnit com a descrição da assinatura e a operação de cada um:

  • assertTrue(String errorMessage, boolean booleanExpression): Verifica se a expressão booleana é verdadeira. Caso não seja, adiciona a mensagem de erro na lista a ser mostrada após a execução dos testes.
  • assertFalse(String errorMessage, boolean booleanExpression): Verifica se a expressão booleana é falsa. Caso não seja, adiciona a mensagem de erro na lista a ser mostrada após a execução dos testes.
  • assertEquals(String errorMessage, Object a, Object b): Verifica se o objeto "a" é igual ao objeto "b". Caso não seja, adiciona a mensagem de erro na lista a ser mostrada após a execução dos testes.
  • assertNull(String errorMessage, Object o): Verifica se o objeto é nulo. Caso não seja, adiciona a mensagem de erro na lista a ser mostrada após a execução dos testes.

Para ver a lista completa dos métodos de assertivas consulte http://www.junit.org/junit/javadoc/index.htm.

Escrevendo um teste com JUnit utilizando Eclipse

O Eclipse nos oferece um plugin para o JUnit, o qual é padrão para essa IDE Java, dessa forma não é preciso instalar, configurar e ativar esse plugin para que possa ser utilizado. Vamos ver como o Eclipse pode auxiliar no processo de construção de uma classe de teste, para uma classe já existente, a partir do plugin para o JUnit. A única coisa necessária é visualizar a view do JUnit, no menu principal em Window -> Show View -> Other. Depois basta expandir a opção "Java" e clicar em JUnit.

O primeiro passo é clicar com o botão direito sobre o pacote que armazenará a classe de teste.

Agora selecione TestCase na opção JUnit para o tipo de classe a ser criada. Clique no botão "next".

Nesse momento selecionaremos a classe que será testada, para isso clique no botão "browser" ao lado do campo "Test class" e selecione a classe.

Após clicar no botão "Ok" automaticamente serão atribuídos nomes para os campos "Test case" (nome da classe de teste) e "Test class" (a classe a ser testada). Nesse momento podemos selecionar os checkbox de setUp() e tearDown(). Para esse exemplo não selecionaremos nenhum dos dois, pois não nos interessa qualquer tipo de ação antes do testes serem executados nem após suas execuções.

Clicando no botão "Next" nos será apresentada a opção de selecionar qual/quais o método que iremos testar na classe selecionada no campo "Test class". Selecionamos o método desejado (isValid()) e clicamos no botão "Finish".

Com isso foi criada a classe Java para teste. Essa classe (InscricaoEstadualTest) esta com o método de teste referente ao método selecionado em branco, ou seja, sem implementação. Nesse momento é necessário escrever o método que atende a necessidade de validação do processo executado pelo método isValid da classe InscricaoEstadual. Após a implementação desse método a classe de teste estará com o seguinte conteúdo.

/* 
* Created on 22/01/2004
*
* Néki Technologies
*/

package com.neki.test;

import com.neki.InscricaoEstadual;
import junit.framework.TestCase;

/**
* @author Néki Team
*
*/

public class InscricaoEstadualTest extends TestCase {

/**
* Constructor for ValidarInscricaoEstadualTest.
* @param arg0
*/

public InscricaoEstadualTest(String arg0) {
super(arg0);
}

public void testIsValid() {
boolean b = InscricaoEstadual.isValid("81501319");
assertTrue("Inscricao Estadual invalida",b);
}
}

Para executar o teste basta abrir a classe no editor e selecionar a opção de Run As -> JUnit test no menu principal.

Hierarquia dos testes

Especificando a hierarquia dos testes a serem executados com junit.framework.TestSuite utilizando Eclipse

Existe uma forma de executar todas as classes de testes que você queira de uma só vez. Para isso é necessário criar um grupo onde serão adicionadas essas classes de testes. Esse grupo é implementado por uma classe chamada junit.framework.TestSuit, a qual também é suporta pelo plugin JUnit do Eclipse. Adotamos como padrão para o nome da classe Java de suíte de testes a utilização do sufixo "Tests" após o nome da classe escolhido.

A seguir será mostrada a forma simples de criar uma suíte de testes com o Eclipse e o plugin do Junit.

O primeiro passo é clicar com o botão direito sobre o pacote que armazenará a suíte de teste. Essa classe deverá estar no mesmo pacote das classes de teste que farão parte da suíte de teste.

Agora selecione TestSuite na opção JUnit para o tipo de classe a ser criada. Clique no botão "next".

Nesse momento selecionaremos as classes de teste que farão parte da suíte de teste e adicionaremos o nome da mesma com o sufixo "Tests". Após isso clique no botão "Finish". É importante lembrar que todas as classes que estão no pacote onde a suíte de testes será criada e são subclasses de junit.framework.TestCase aparecerão na caixa "Test Classes to include in Suite" para você selecionar quais farão parte da suíte.

/* 
* Created on 23/01/2004
*
* Néki Technologies
*/

package com.neki.test;

import junit.framework.Test;
import junit.framework.TestSuite;

/**
* @author Administrador
*
*/

public class PrincipaisTests {
public static Test suite() {
TestSuite suite = new TestSuite("Teste para com.neki.test");
//$JUnit-BEGIN$
suite.addTest(new TestSuite(CheckoutCvsTest.class));
suite.addTest(new TestSuite(ProjetosTest.class));
suite.addTest(new TestSuite(InscricaoEstadualTest.class));
//$JUnit-END$
return suite;
}
}

O método de addTest de TestSuite aceita como parâmetro um objeto que implemente a interface Test. Ambos os testes adicionados são subclasses de TestCase, a qual implementa a interface Test. No primeiro e segundo método addTest chamados, estão simplesmente sendo adicionados dois testes para a lista de testes que a instância de TestSuite irá executar.

Saídas Esperadas

Após a execução de um teste unitário, seja ele uma classe de teste ou uma suíte de teste, o Eclipse mostrará a view relacionada ao plugin JUnit. Nessa view existe uma barra de analise, que pode ser preenchida com a cor verde ou vermelha, dependendo do resultado dos testes.

Barra verde - caso de sucesso

Após a execução de uma classe de teste, conforme vimos anteriormente para a classe "InscricaoEstadualTest", será mostrada uma barra verde indicando sucesso no teste.

Barra vermelha - houve falhas

Para visualizarmos falha em um processo de teste e analisar a resposta que o plugin do JUnit nos fornece, vamos alterar o valor do parâmetro utilizado na classe de teste InscricaoEstadualTest de 81501319 para 81501310.

Feedback

Por favor, dê a sua opinião a respeito do conteúdo apresentado. Correções, sugestões ou qualquer outra informação relevante é de suma importância para nós e será muito bem vinda.

Contato: owner@hotwork.dev.java.net

Nenhum comentário: