terça-feira, 23 de março de 2010

Para que serve o teste de software?

No final de janeiro eu fui a Melbourne (na Florida, não na Austrália) participar do Workshop on Teaching Software Testing, onde professores acadêmicos, instrutores de cursos em empresas e testadores praticantes da indústria se reuniram para trocar suas experiências sobre ensino de teste. O tema de discussão este ano era o teste com base no código. Éramos 22 participantes dos Estados Unidos, Canada e eu do Brasil. Seguem alguns pontos que foram abordados nas discussões e que eu achei relevantes.

A maioria dos programadores e principalmente estudantes norte-americanos dos programas de graduação em Engenharia de Software não acham que precisam testar seus programas com rigor - aqui rigor não significa nem formalismo matemático, nem exaustão, uma vez que é impossível, mas sim uma forma sistemática do programador testar seus programas munido de técnicas apropriadas. A maioria dos programadores dá um tiro no próprio pé fazendo alguns poucos testes aleatórios. Por mais que a universidade tente mostrar a necessidade e que o teste seja também papel do desenvolvedor/programador, os alunos só se convencem disso quando vão para o mercado.

As experiências no ensino de desenvolvimento dirigido por testes (TDD) são frustrantes. A maior parte dos alunos não utiliza a metodologia pois TDD não pode ser considerada um técnica por si só. E uma vez que é apenas uma metodologia, é dificil para os professores exigirem dos alunos a aplicação da mesma. Mesmo sabendo que serão penalizados, os alunos não aplicam o TDD e só criam os casos de teste após terem escrito o código. Estou falando de TDD quando o assunto é teste, mas sabemos que as grandes contribuições de TDD estão na evolução de um bom projeto, no entendimento maior do problema em questão antes da codificação. Desta forma, os alunos que imaginam que só precisam criar casos de teste e que tanto faz antes como depois do código perdem o melhor da metodologia. São os profissionais com essa atitude que chegam ao mercado.  Se TDD fosse aplicado desde as primeiras disciplinas de programação, talvez assim os alunos percebessem a
vantagem da abordagem. Algumas universidades americanas vão investir neste caminho.

No passado, teste servia o propósito limitado de detectar erros nos programas. Através da quatidade de erros encontrados no programa era possível derivar dos testes um indicador da qualidade do programa. Esse era o tempo em que os programas eram monolíticos ou compostos de pequenos módulos. Hoje em dia cada componente utilizado é de uma complexidade grande e possui suas próprias características intrínsecas. Princípios de ocultação de informacao e interfaces bem definidas foram aplicados e a engenharia de software se orgulha destes avanços. No entanto, o comportamento de um determinado módulo ou componente precisa ser descoberto pelo programador que usa este módulo ou componente, e a maneira de descobrir este comportamento é através de testes. Muitas vezes a documentação é escassa ou descreve bem o que se espera que o componente faça, mas na verdade o comportamento dele é um pouco diferente. Este propósito do teste de descobrir ou certificar o comportamento dos componentes é completamente diferente do propósito de detectar defeitos. Quais técnicas de teste devem ser usadas neste caso?

Produzir casos de teste relevantes continua sendo o problema mais difícil de se resolver em testes, principalmente no teste considerando o código. Como o Torsten disse anteriormente, se o foco de teste passa a ser a cobertura no sentido que passei por aqui, é melhor projetar os casos de teste antes de escrever o código e com base na sua especificação. Mas será que usaríamos os mesmos casos de teste para testar as três versões de código abaixo da função fatorial? (exemplos de código fornecidos por  Nawwar Kabbani - participante do workshop.)

public intfactorial (intn) {
  if(n == 0)
    return 1;
  else
    return * factorial(n –1);
}


public int factorial (int n) {
  if (n == 0) return 1;
  else if (n == 1) return 1;
  else if (n == 2) return 2;
  else if (n == 3) return 6;
  else if (n == 4) return 24;
  else if (n == 5) return 120;
  else if (n == 6) return 720;
  else if (n == 7) return 5040;
  else if (n == 8) return  40320;
  else if (n == 9) return  362880;
  else if (n == 10) return 3628801; // this one looks odd!
  else if (n == 11) return 39916800;
  else if (n == 12) return 479001600;
  else return -1; // or throw an exception..
}


private static final int FACTORIAL_CACHE[] = new  int[]
{1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880,
362801, 39916800, 479001600};

public int factorial (int n) {
  return FACTORIAL_CACHE[n];
}

Com certeza, vendo os três códigos conseguimos pensar em casos de testes que gostaríamos de fazer. Alguns com base na definição do fatorial, outros com base no código que foi apresentado. No último caso a cobertura não vai ajudar em nada.

Para terminar, eu recomendo fortemente o mini curso do Doug Hoffman que participará do Encontro Brasileiro de Testes de Software (IV EBTS) , ele na minha opiniao é um grande praticante com muitas estórias de sucesso sobre testes de software para contar. Seu objetivo é coletar várias estórias para compor um livro que possa ajudar a comprovar os diversos propósitos de testes e sua necessidade para a sobrevivência dos programadores.

Nenhum comentário:

Postar um comentário