Esse artigo faz parte da revista Java Magazine edição 50. Clique aqui para ler todos os artigos desta edição

>que aqui para ler esse artigo em PDF.imagem_pdf.jpg 

Testes Unitários Eficazes

Projeto de testes, critérios de qualidade e boas práticas

 

Os testes hoje fazem parte da rotina de todo desenvolvedor, e a gama de ferramentas disponíveis para esse fim é imensa. Apesar disso, nem sempresão desenvolvidos da melhor forma possível, com tempo e esforço sendo gastos na criação de testes com pouco valor. Neste artigo apresentamos, através de um exemplo completo, os desafios com que se deparam os desenvolvedores durante a atividade de testes e indicamos como criar casos de teste mais eficazes. Apesar de o foco ser no teste de unidade, com auxílio do JUnit 4 e do Emma, os conceitos são válidos para todas as fases de testes.

 

Alguns conceitos básicos

Vamos começar relembrando alguns conceitos. Os testes de unidade são a primeira fase de testes do software, sendo normalmente executados pelo desenvolvedor. Essa fase é, sem dúvida, a mais conhecida e a que possui mais ferramentas de apoio (JUnit, TestNG, JMock etc.). Tais ferramen tas facilitam e automatizam a execução dos testes e a análise de resultados, mas em geral não contemplam uma atividade fundamental: o projeto dos casos de teste.

De forma resumida, podemos dizer que um caso de teste descreve o comportamento esperado do programa em teste sob circunstâncias controladas. É constituído, basicamente, por uma entrada conhecida e um resultado esperado1. A entrada pode ser um parâmetro, uma mensagem numa fila, ou qualquer dado que alimente a unidade em teste2. O resultado esperado, por sua vez, pode ser uma mensagem noconsole, um registro no banco de dados, a geração de uma exceção e assim por diante. Quando o caso de teste é executado e o resultado obtido corresponde exatamente ao esperado, diz-se que o caso de teste “passou”. Caso contrário, considera-se que o caso de teste “falhou”.

Sabemos que o principal objetivo dos testes de software é encontrar erros no programa. Mas quando isso não ocorre, vemo-nos diante de um dilema: o software realmente não possui defeitos, ou o teste executado não foi bom o suficiente para encontrar os defeitos existentes? E essa questão leva a outra: quando determinar que a atividade de testes pode ser finalizada e o software possui qualidade suficiente?

A eficácia dos casos de teste é, portanto, um fator determinante para comprovar a qualidade do software. Como veremos a seguir, existem técnicas e critérios de teste que podem ser utilizados para indicar quando o conjunto de testes elaborado possui qualidade suficiente.

 

Nota 1: Um caso de teste, é claro, ainda possui muitas outras informações como identificador, objetivo, pré-condições, dependências, ambiente de execução etc.

 

 

Nota 2: Unidade em teste (UUT Unit Under Test), como indica o nome, especifica o que está sendo testado (um método, uma classe etc.).

 

 

Um exemplo

Utilizaremos uma aplicação de exemplo para mostrar desafios existentes no teste de software e como contorná-los. O exemplo apresentado converte valores por extenso e sua especificação é descrita a seguir:

O software converte um valor monetário descrito sob o formato “9999,99” (uma string) em sua representação textual (extenso). A parte inteira (Reais) pode conter de zero a quatro dígitos; a parte decimal (centavos) é opcional, mas quando utilizada deve conter zero ou dois dígitos. Caso o valor não possua parte decimal, a parte decimal pode ser representada através de um único dígito zero.

A modelagem da aplicação é direta: a classe abstrata Extenso (Listagem 1) implementa o algoritmo que transforma valores numéricos em textos, através do método protected numeroExtenso(). Esse método suporta valores de 1 a 9999.

 

Listagem 1. Conversão de números em extenso (contendo defeitos)

 

package br.com.jm.extenso;

 

public abstract class Extenso {

 

private static String[] unidades = { “”, “um”, “dois”, “três”,

               “quatro”, “cinco”, “seis”, “sete”, “oito”, “nove” };

 

private static String[] dezenas1 = { “”, “onze”, “doze”, “treze”,

               “catorze”, “quinze”, “dezesseis”, “dezesete”, “dezoito”, “dezenove” };

 

private static String[] dezenas2 = { “”, “dez”, “vinte”, “trinta”,

              “quarenta”, “cinquenta”, “sessenta”, “setenta”, “oitenta”, “noventa” };

 

private static String[] centenas = { “”, “cem”, “duzentos”,

“trezentos”, “quatrocentos”, “quinhentos”, “seiscentos”,

             “setecentos”, “oitocentos”, “novecentos” };

 

private int[] particao;

private String[] texto;

 

public abstract String extenso(String valor)

    throws ValorInvalidoException;

 

protected boolean numeroValido(String numero) {

  boolean ok = true;

    if (numero.length() > 4) {

       ok = false;

        } else {

           if (!””.equals(numero)) {

                try {

                    Integer.parseInt(numero);

                     } catch (NumberFormatException e) {

                      ok = false;

                  ...

Quer ler esse conteúdo completo? Tenha acesso completo