Conceito: Lista de Idéias de Teste
Uma lista de idéias de teste é classificada em ordem decrescente de importância e é associada a estratégias de teste específicas utilizadas para criar testes executáveis.
Relacionamentos
Descrição Principal

Introdução

As informações utilizadas em projetos de testes são reunidas de muitos lugares: modelos de design, interfaces de classificador, diagramas de estados e o próprio código. Em determinado ponto, as informações do documento de origem devem ser transformadas em testes executáveis:

  • entradas específicas fornecidas para o software em teste
  • uma configuração de hardware e software específica
  • inicializado para um estado conhecido
  • resultados específicos esperados

é possível ir diretamente das informações do documento de origem para os testes executáveis, mas é freqüentemente útil incluir uma etapa intermediária. Nesta etapa, as idéias de teste são gravadas em uma Lista de Idéias de Teste, que é utilizada para criar testes executáveis.

O que São Idéias de Teste?

Uma idéia de teste (às vezes chamada de requisito de teste) é uma instrução breve sobre um teste que poderia ser desempenhado. Como um exemplo simples, vamos considerar uma função que calcula uma raiz quadrada e gera algumas idéias de teste:

  • fornecer um número um pouco menor que zero como entrada
  • fornecer zero como entrada
  • testar um número que tenha uma raiz quadrada perfeita, como 4 ou 16 (o resultado é exatamente 2 ou 4?)

Cada uma dessas idéias poderia ser facilmente convertida em um teste executável com as descrições exatas das entradas e dos resultados esperados.

Há duas vantagens nessa forma intermediária menos específica:

  • as idéias de teste são mais revisáveis e compreensíveis que os testes completos. É mais fácil entender o raciocínio por trás delas
  • as idéias de teste suportam testes mais poderosos, conforme descrito posteriormente no título Design de Teste Utilizando a Lista

Todos os exemplos de raiz quadrada descrevem entradas, mas as idéias de teste podem descrever qualquer elemento de um teste executável. Por exemplo, "imprimir em uma LaserJet IIIp" descreve um aspecto do ambiente de teste a ser utilizado para um teste, como faz o "teste com banco de dados completo", entretanto, essas idéias de teste mais recentes são muito incompletas: Imprimir o quê na impressora? Fazer o quê com esse banco de dados completo? No entanto, elas garantem que idéias importantes não sejam esquecidas; idéias que serão descritas com mais detalhes posteriormente no design de teste.

Freqüentemente, as idéias de teste têm como base os modelos de falha; noções de que as falhas são plausíveis no software e como essas falhas podem ser melhor descobertas. Por exemplo, considere fronteiras. É seguro presumir que a função de raiz quadrada pode ser implementada de modo semelhante a este:

double sqrt(double x) {
    if (x < 0)
      // erro de sinal
    ...

Também é plausível que < seja digitado incorretamente como <=. As pessoas sempre cometem esse tipo de erro, então vale a pena verificar. A falha não pode ser detectada com X tendo o valor 2, porque a expressão incorreta (x<=0) e a expressão correta (x<0) estarão na mesma ramificação da instrução if. De modo semelhante, fornecendo a X o valor -5, não será possível localizar a falha. A única maneira de localizá-la é fornecer a X o valor 0, que justifica a segunda idéia de teste.

Nesse caso, o modelo de falha é explícito. Em outros casos, é implícito. Por exemplo, sempre que um programa manipula uma estrutura vinculada, convém testá-la com uma circular. É possível que muitas falhas levem a uma estrutura circular manipulada incorretamente. Para as finalidades de teste, elas não precisam ser enumeradas, basta saber que uma falha é provavelmente suficiente para justificar a execução do teste.

Os seguintes links fornecem informações sobre como obter idéias de teste de diferentes tipos de modelos de falha. Os dois primeiros são modelos de falha explícita; o último utiliza implícitas.

É possível aplicar esses modelos de falha a vários produtos de trabalho diferentes. Por exemplo, o primeiro descreve o que fazer com as expressões Booleanas. Essas expressões podem ser encontradas em códigos, em condições de guarda, em diagramas de estados e de seqüência e nas descrições de idioma nacional dos comportamentos de método (como você pode encontrar em uma API publicada).

Ocasionalmente, também é útil ter diretrizes para produtos de trabalho específicos. Consulte Orientação: Idéias de Teste para Gráficos de Estado e Fluxogramas.

Uma Lista de Idéias de Teste específica pode conter idéias de teste de muitos modelos de falha e esses modelos de falha podem ser derivados de mais de um produto de trabalho.

Design de Teste Utilizando a Lista

Suponha que você esteja projetando testes para um método que procura uma seqüência de caracteres em um conjunto seqüencial. Ele pode obedecer ou ignorar maiúsculas e minúsculas na pesquisa, retornando o índice da primeira correspondência encontrada ou -1 se nenhuma for encontrada.

int Collection.find(String string,
            Boolean ignoreCase);

Estas são algumas idéias de teste para o método:

  1. correspondência localizada na primeira posição
  2. correspondência localizada na última posição
  3. nenhuma correspondência localizada
  4. duas ou mais correspondências localizadas na coleta
  5. o caso é ignorado; correspondência localizada, mas não corresponderia se o caso fosse obedecido
  6. o caso é obedecido; uma correspondência exata é localizada
  7. maiúsculas e minúsculas obedecidas; foi ignorada uma seqüência de caracteres em que haveria correspondência se as maiúsculas e minúsculas fossem ignoradas

Seria simples implementar sete testes, um para cada idéia de teste. Contudo, idéias de teste distintas podem ser combinadas em um único teste. Por exemplo, o seguinte teste satisfaz as idéias de teste 2, 6 e 7:

Configuração: coleta inicializada para ["surgimento", "Surgimento"]
Chamada: collection.find("Surgimento", falsa)
Resultado esperado: o valor de retorno é 1 (seria 0 se o "surgimento" não fosse ignorado)

Tornar as idéias de teste não específicas as torna mais fáceis de combinar.

É possível satisfazer todas as idéias de teste em três testes. Por que três testes que satisfazem a sete idéias de teste seriam melhores que sete testes separados?

  • Quando você estiver criando um grande número de testes simples, é comum criar o teste N+1 copiando o teste N e o ajustando o suficiente para atender à nova idéia de teste. O resultado, especialmente em um software mais complexo, é que o teste N+1 provavelmente exercita o programa quase do mesmo jeito que o teste N. Percorre quase exatamente o mesmo caminho pelo código.

    Um número menor de testes, cada um atendendo várias idéias de teste, não permite uma abordagem de "cópia e ajuste". Cada teste será de algum modo diferente do último, exercitando o código de maneiras diferentes e percorrendo caminhos diferentes.

    Por que isso seria melhor? Se a Lista de Idéias de Teste estivesse completa, com uma idéia de teste para cada falha do programa, a maneira como os testes foram escritos não seria importante. Mas, na lista, sempre faltam algumas idéias de teste que poderiam localizar erros. Fazendo com que cada teste faça coisas diferentes do último e incluindo uma variedade aparentemente desnecessária, você aumenta a chance de que, por azar, um teste passe por cima do erro. Na verdade, testes menores e mais complexos aumentam a chance de o teste satisfazer a uma idéia de teste que você não sabia ser necessária.

  • Às vezes, ao criar testes mais complexos, novas idéias de testes surgem. Isso acontece com menos freqüência do que com os testes simples porque muito do que você está fazendo é exatamente como o último teste, sua criatividade diminui.

No entanto, há motivos para não criar testes complexos.

  • Se cada teste satisfaz uma única idéia de teste e o teste para a idéia 2 falha, você imediatamente sabe a causa mais provável: o programa não manipula uma correspondência na última posição. Se um teste satisfaz as idéias 2, 6 e 7, o isolamento do defeito é mais difícil.

  • É mais difícil compreender e manter testes complexos. O propósito do teste é menos óbvio.

  • É mais difícil criar testes complexos. Criar um teste que satisfaça a cinco idéias de teste normalmente leva mais tempo que criar cinco testes que satisfaça a apenas uma. Além disso, é mais fácil cometer erros, pensar que está satisfazendo todas as cinco, mas está satisfazendo apenas quatro.

Na prática, você precisa encontrar um equilíbrio razoável entre complexidade e simplicidade. Por exemplo, os primeiros testes aos quais você sujeita o software (normalmente os testes de regressão) devem ser simples, fáceis de compreender e de manter, e com o objetivo de capturar os problemas mais óbvios. Os testes posteriores devem ser mais complexos, mas não tão complexos que não possam ser mantidos.

Depois de concluir um conjunto de testes, é bom verificar os erros característicos de design de teste discutidos em Conceito: Teste de Desenvolvedor.

Utilizando Idéias de Teste Antes de Testar

Uma Lista de Idéias de Teste é útil para revisões e inspeções dos produtos de trabalho de design. Por exemplo, considere isso como parte de um modelo de design mostrando a associação entre as classes Departamento e Funcionário.

Imagem de Exemplo do Modelo de Design

Figura 1: Associação entre as Classes Departamento e Funcionário

As regras para a criação de idéias de teste a partir desse modelo fariam com que você considerasse o caso em que um departamento possui vários funcionários. Passando por um design e perguntando "e se, a essa altura, o departamento tiver muitos funcionários?", você pode descobrir erros de design ou de análise. Por exemplo, você poderá perceber que apenas um funcionário de cada vez pode ser transferido entre departamentos. Isso pode ser um problema se a corporação estiver propensa a eliminar reorganizações em que muitos funcionários precisam ser transferidos.

Essas falhas, casos em que uma possibilidade não foi percebida, são chamadas de falhas de omissão. Como nas falhas em si, você provavelmente omitiu testes que detectam essas falhas no seu esforço de teste. Por exemplo, consulte [GLA81], [OST84], [BAS87], [MAR00] e outros estudos que mostram com que freqüência as falhas de omissão escapam para a implementação.

A função de teste nas atividades de design é discutida mais adiante em Conceito: Teste Anterior ao Design.

Idéias de Teste e Rastreabilidade

A rastreabilidade é uma questão de equilíbrio de fatores. Seu valor justifica o custo de mantê-la? Essa questão precisa ser considerada durante a Tarefa: Definir as Necessidades de Avaliação e de Rastreabilidade.

Quando a rastreabilidade vale a pena, convém rastrear os testes em relação aos produtos de trabalho que os inspiraram. Por exemplo, pode haver rastreabilidade entre uma API e seus testes. Se a API for alterada, você saberá quais testes também deverão ser alterados. Se o código (que implementa a API) é alterado, você sabe quais testes executar. Se um teste o intrigar, você poderá encontrar a API que ele pretende testar.

A Lista de Idéias de Teste adiciona outro nível de rastreabilidade. Você pode rastrear desde um teste até as idéias de teste a que ele satisfaz e, em seguida, desde as idéias de teste até o produto de trabalho original.