Conceito: Lista de Ideias de Teste
Relacionamentos
Elementos Relacionados
Descrição Principal

Introdução

As informações usadas no design de testes são obtidas de várias fontes: modelos de design, interfaces classificadoras, gráficos de estado e o próprio código. Em algum momento, essa fonte de informações documentais deve ser transformada em testes executáveis:

  • entradas específicas fornecidas ao software sob teste
  • em uma determinada configuração de hardware e software
  • definida inicialmente em um estado conhecido
  • com resultados específicos esperados

É possível ir diretamente da fonte de informações documentais aos testes executáveis, mas é útil acrescentar uma etapa intermédia. Nesta etapa, as ideias de teste são escritas em uma Lista de Ideias de Teste, que é usada para criar os testes executáveis.

O que são as ideias de teste?

Uma ideia de teste (também conhecida como um requisito de teste) é uma declaração resumida de um teste que pode ser realizado. Como simples exemplo, vamos considerar uma função que calcula a raiz quadrada e que tenha as seguintes ideias de teste:

  • fornecer um número que é inferior a zero como entrada
  • fornecer zero como entrada
  • testar um número que seja um quadrado perfeito, tal como 4 ou 16 (o resultado é exatamente 2 ou 4?)

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

Existem duas vantagens para esta forma intermediaria menos específica:

  • As ideias de teste são mais fáceis de revisar e entender do que os testes completos - é mais fácil entender o raciocínio por trás delas
  • as ideias de teste suportam testes mais poderosos, descritos abaixo sob o título Design de Teste Usando a Lista

Os exemplos da raiz quadrada descrevem as entradas, mas as ideias 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 usado em um teste, tal como "testar com o banco de dados completo"; Entretanto, estas últimas idéias de teste são muito incompletas em si: Imprimir o que na impressora? Fazer o que com o banco de dados completo? Entretanto, elas asseguram que ideias importantes não foram esquecidas; ideias que serão descritas com mais detalhes posteriormente, no design de testes.

As ideias de teste são muitas vezes baseadas em modelos de falhas; noções de quais falhas são plausíveis no software e como elas podem ser melhor descobertas. Por exemplo, considere as fronteiras. É seguro assumir que a função raiz quadrada pode ser implementada tal como:

    double sqrt(double x) {
        if (x < 0)
        // signal error
        ...

Também é plausível que o < seja digitado incorretamente como <=. As pessoas normalmente cometem este tipo de erro, por isso é importante verificar. A falha não pode ser detectada com X tendo o valor 2, porque tanto a expressão incorreta (X<= 0) como a expressão correta (X<0) seguirão pelo mesmo ramo da declaração if. Da mesma forma, se X tiver o valor -5, a falha não será encontrada. A única forma de encontrá-la é atribuir a X o valor 0, o que justifica a segunda ideia de teste.

Neste caso, o modelo de falha é explícito. Em outros casos, é implícito. Por exemplo, sempre que um programa manipula uma estrutura encadeada, é bom testá-lo com uma estrutura circular. É possível que muitas falhas possam conduzir a uma estrutura circular mal tratada. Para os propósitos do teste, elas não precisam ser enumeradas - é suficiente saber que qualquer falha é o bastante para que valha a pena executar o teste.

Os links a seguir contêm informações sobre como obter ideias de teste de diferentes tipos de modelos de falha. Os dois primeiros são modelos de falha explícitos; o último usa um implícito.

Estes modelos de falha podem ser aplicados em diversos artefatos. Por exemplo, o primeiro descreve o que fazer com expressões Booleanas. Tais expressões podem ser encontradas no código, em condições de guarda, em gráficos de estado e diagramas de sequência e em descrições em linguagem natural dos comportamentos do método (tal como você pode encontrar em uma API publicada).

Ocasionalmente também é útil ter diretrizes para artefatos específicos. Veja Guideline: Ideias de Teste para Gráficos de Estado e Diagramas de Fluxo.

Uma Lista especial de Ideias de Teste pode conter ideias de teste de muitos modelos de falha, e os modelos de falha podem ser derivados de mais de um artefato.

Design de Testes Usando a Lista

Vamos supor que você esteja projetando testes para um método que pesquise uma string em uma coleção sequencial. Ele pode tanto considerar a caixa ou ignorá-la em sua pesquisa, e retornar a posição da primeira correspondência encontrada ou -1 se nenhuma for encontrada.

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

Aqui estão algumas ideias de teste para este método:

  1. correspondência encontrada na primeira posição
  2. correspondência encontrada na última posição
  3. nenhuma correspondência encontrada
  4. duas ou mais correspondências encontradas na coleção
  5. com a caixa ignorada a correspondência foi encontrada, mas não seria se a caixa fosse considerada
  6. com a caixa considerada, uma correspondência exata foi encontrada
  7. com a caixa considerada, não foi encontrada uma string que caso a caixa fosse ignorada ela seria

Seria simples implementar estes sete testes, um para cada ideia de teste. Entretanto, diferentes ideias de teste podem ser combinadas em um único teste. Por exemplo, o teste a seguir satisfaz as ideias de teste 2, 6 e 7:

Inicialização: a coleção é inicializada com [ "alvorada", "Alvorada"]
invocação: collection.find("Alvorada", false)
Resultado esperado: o valor de retorno é 1 (seria 0 se "alvorada" não fosse desprezada)

Fazer as ideias de teste não específicas torna-as mais fáceis de combinar.

É possível satisfazer todas as ideias de teste com três testes. Porque três testes, que satisfazem as sete ideias de teste, são melhores do que sete testes separados?

  • Quando você cria uma grande quantidade de testes simples, é comum criar o teste N+1, copiando o teste N e ajustando-o apenas o suficiente para satisfazer a nova ideia de teste. The result, especially in more complex software, is that test N+1 probably exercises the program in almost the same way as test N. It takes almost exactly the same path through the code.

    Uma menor quantidade de testes, cada um satisfazendo várias ideias de teste, não permite uma abordagem "copiar e adaptar". Cada teste será um pouco diferente do último, exercitando o código de formas diferentes e percorrendo caminhos diferentes.

    Por que isso seria melhor? Se a Lista de Ideias de Teste estivesse completa, com uma ideia de teste para cada falha no programa, não seria importante a forma como você iria escrever os testes. Mas sempre faltam algumas ideias de teste na lista que poderiam encontrar erros. Se cada teste fizer coisas muito diferentes do último - acrescentando variedade aparentemente desnecessária - você aumenta a chance de um dos testes encontrar um erro escondido. Com efeito, os testes menores e mais complexos aumentam a chance de satisfazer uma ideia de teste que você não sabia que era necessária.
  • Às vezes quando você está criando testes mais complexos, novas ideias de teste vêm à mente. Isto acontece em menor frequência com testes simples, porque grande parte do que você está fazendo é exatamente igual ao que você fez no último teste, o que embota sua mente.

Entretanto, existem razões para não criar testes complexos.

  • Se cada teste satisfizer uma única ideia de teste e o teste para a ideia 2 falhar, você saberá imediatamente a causa mais provável: o programa não tratou uma correspondência na última posição. Se um teste satisfizer as ideias 2, 6, e 7 então isolar a falha será mais difícil.
  • Os testes complexos são mais difíceis de entender e manter. A intenção do teste é menos evidente.
  • Os testes complexos são mais difíceis de criar. A construção de um teste que satisfaça cinco ideias de teste, normalmente leva mais tempo do que a construção de cinco testes que satisfaçam cada uma. Aliás, é muito mais fácil cometer erros - pensar que está satisfazendo todas as cinco quando está apenas satisfazendo quatro.

Na prática, você deve encontrar um equilíbrio razoável entre a complexidade e a simplicidade. Por exemplo, os primeiros testes que você submeteu o software (normalmente os testes fumaça) devem ser simples, fáceis de entender e manter e destinados a capturar os problemas mais óbvios. Os testes posteriores devem ser mais complexos, mas não tão complexos que não possam ser mantidos.

Após terminar um conjunto de testes, é bom verificar os enganos característicos de design de teste discutidos em Concept: Testes de Desenvolvedor.

Usando as Ideias de Teste Antes da Execução dos Testes

Uma Lista de Ideias de Teste é útil para revisões e inspeções nos artefatos de design. Por exemplo, considere esta parte de um modelo de design que mostra a associação entre as classes Departamento e Empregado.

Figura 1: Associação entre as Classes Departamento e Empregado

As regras para a criação de ideias de teste a partir de um modelo desse tipo iriam lembrar-lhe de considerar o caso de um departamento ter muitos empregados. Ao percorrer um design perguntando "e se, neste ponto, o departamento tiver muitos empregados?", você poderá descobrir erros de design ou análise. Por exemplo, você pode perceber que só um empregado pode ser transferido entre departamentos por vez. Isto pode ser um problema se a corporação está propensa a executar reorganizações onde muitos trabalhadores necessitem ser transferidos.

Tais falhas, casos em que uma possibilidade foi esquecida, são chamadas de falhas de omissão. Tal como as próprias falhas, você provavelmente omitiu os testes que detectam estas falhas, do seu esforço de testes. Por exemplo, veja [GLA81], [OST84], [BAS87], [MAR00] e outros estudos que mostram a frequência em que falhas e omissões só aparecem na implantação.

O papel dos testes nas atividades de design é mais discutido em Concept: Design Teste-Primeiro.

Ideias de Teste e Rastreabilidade

A rastreabilidade é uma questão de análise. O seu benefício vale mais que o custo de mantê-la? Esta questão tem que ser considerada durante a Atividade: Definir as Necessidades de Avaliação e Rastreabilidade.

Quando a rastreabilidade vale a pena, é convencional rastrear os testes com os artefatos que os inspiraram. Por exemplo, você poderia ter uma rastreabilidade entre uma API e seus testes. Se a API mudar, você saberá quais testes terão que ser alterados. Se o código (que implementa a API) mudar, você saberá quais testes deverão ser executados. Se um teste lhe confundir, você poderá encontrar a API a que ele se destina.

A Lista de Ideias de Teste acrescenta outro nível de rastreabilidade. Você pode rastrear um teste à ideia de teste que ele satisfaz e, então, ao artefato original.