Esta diretriz descreve alguns tipos de teste. Para processar estes tipos de teste é necessário definir, e então
executar, uma série de testes no código fonte. Um teste de desenvolvedor é um teste único que necessita ser executado.
É importante ter muitos testes automatizados, com scripts que possam ser lidos pelas pessoas, para ser possível
implementar os casos de teste de desenvolvedor e os scripts que incluam as informações abaixo. Um script de teste são
passos reais, podendo ser procedimentos escritos a serem seguidos ou o código fonte de um teste. Os scripts de teste de
desenvolvedor são executados nos alvos de teste, podendo ser: uma unidade de código fonte ou uma parte mais complexa do
sistema (tal como um componente) ou o próprio sistema como um todo para testar alguma questão de desenvolvedor tal como
a integração.
Teste de Regressão
O teste de regressão é o ato de assegurar que as mudanças no código não afetaram a funcionalidade existente de forma
adversa. É importante reconhecer que o desenvolvimento incremental faz com que o teste de regressão seja crítico.
Sempre que você liberar uma aplicação, você deve assegurar que as funcionalidades pré-existentes continuem funcionando.
O fato de você liberar aplicações com maior freqüência quando usa a abordagem incremental, significa que os testes de
regressão tornam-se muito mais importantes. A execução dos testes de regressão é a primeira coisa que você deve pensar
pelas seguintes razões:
-
Você quer ter a capacidade de modificar o código e saber que é possível reexecutar os testes para ver se alguma
parte foi danificada.
-
Os usuários ficam muito irritados quando descobrem que aquilo que estava funcionando previamente não funciona mais.
O Teste de regressão é razoavelmente direto - é necessário executar somente os casos de teste existentes na nova versão
do código. As ferramentas de teste de regressão ajudam imensamente porque são projetadas com base na execução dos
testes de regressão. Entretanto, existem desafios em potencial no teste de regressão:
-
Quando a produção de código for alterada para melhoria ou refatoração, você necessitará retrabalhar os casos de
teste existentes associados a esse código.
-
Se as atualizações afetarem somente um componente do sistema, será necessário que você execute somente os casos de
teste relacionados a este componente. Embora esta abordagem seja um pouco arriscada, porque as mudanças podem gerar
um impacto maior do que você espera, ela ajuda a reduzir o tempo e o custo dos testes de regressão.
-
Quanto mais artefatos que não sejam de código você mantiver, maior será o esforço para executar testes de regressão
no seu trabalho e conseqüentemente maior o risco para o projeto porque provavelmente haverá um aumento inadequado
dos esforços de teste.
O teste de regressão é tão crítico ao sucesso do projeto quanto um desenvolvedor ágil. Muitos desenvolvedores de
software usam a família xUnit de ferramentas de código aberto, tais como JUnit e VBUnit para testar seus códigos. A
vantagem destas ferramentas é que elas implementam um framework de teste com o qual é possível executar o teste de
regressão em todo o código fonte. As ferramentas comerciais de teste também são opções viáveis.
Técnicas Tradicionais de Teste de Código
Embora as tecnologias de objeto e processual sejam diferentes, diversos conceitos importantes de testes do mundo
processual são válidos, independente da tecnologia utilizada. Estas técnicas tradicionais de teste são:
-
Teste de Caixa-Preta
-
Teste de Caixa-Clara
-
Teste de Valor Limítrofe
-
Teste de Cobertura/Caminho
-
Teste Exploratório
Teste de Caixa-Preta
O Teste de Caixa-Preta, também chamado de teste de interface, é uma técnica em que você cria casos de teste baseado
somente na funcionalidade prevista de um método, de uma classe ou de uma aplicação sem nenhum conhecimento de sua
implementação. Uma forma de definir o teste de caixa-preta é que dada a entrada A você deve obter o resultados previsto
B.
O objetivo do teste de caixa-preta é assegurar que o sistema pode fazer o que deve ser feito, mas não como ele faz. Por
exemplo, se você invocar a função differenceInDays(junho 30 2006, julho 3 2006) o resultado previsto deve ser três. A
criação de testes de caixa-preta é normalmente orientada pelos requisitos do sistema. A idéia básica é olhar para o
requisito e perguntar o que deve ser feito para mostrar que o requisito foi atendido.
A principal vantagem do teste de caixa-preta é que ele permite provar que a aplicação cumpre os requisitos definidos
para ela. A principal desvantagem é que não mostra o que as partes internas do sistema fazem (implicando a necessidade
do teste de caixa-branca).
Teste de Caixa-Branca
O Teste de Caixa-Branca, também chamado de teste de caixa-branca, é baseado na idéia de que o código do programa pode
orientar o desenvolvimento dos casos de teste. O conceito básico é olhar para o código e criar os casos de teste que o
exercitem. Por exemplo, suponha que você tem acesso ao código fonte da função differenceInDays(). Quando você olha para
ele, você vê uma declaração IF testando se as duas datas são do mesmo ano. Se forem do mesmo ano então uma simples
estratégia baseada em datas julianas será usada; se não, uma estratégia mais complexa deverá ser usada. Isto indica que
é necessário pelo menos um teste que use datas do mesmo ano e um que use datas de anos diferentes. Olhando o código, é
possível determinar novos casos de teste para exercitar os diferentes trajetos de sua lógica interna.
A principal vantagem deste conceito é que ele motiva a criação de testes que exercitem linhas específicas do código. As
desvantagens são que ele não assegura que o código cumpre os requisitos reais (implicando a necessidade do teste de
caixa-preta) e que o código do teste torna-se altamente dependente do código da aplicação.
Teste de Valor Limítrofe
Este teste é baseado no conhecimento necessário para testar o código assegurando que ele pode tratar situações incomuns
e extremas. Por exemplo, os casos de teste de valor-limítrofe para a função differenceInDays() devem conter: a passagem
da mesma data nos dois parâmetros, de duas datas extremamente diferentes, do último dia de um ano e o primeiro dia do
ano seguinte e da data 29 de fevereiro de um ano bissexto. A idéia básica é procurar por limites definidos ou nas
regras de negócio ou pelo senso comum, e criar os casos de teste para testar valores iguais ou próximos a eles.
A principal vantagem do teste de valor-limítrofe é que ele motiva a confirmação de que o código do programa pode tratar
casos "incomuns" ou "extremos".
Teste de Cobertura e de Caminho
Dois conceitos "tradicionais" críticos são os testes de Cobertura e de Caminho O teste de cobertura é uma técnica em
que se cria uma série de casos de teste projetados para testar todos os trajetos do código. Muitas vezes, o teste de
cobertura é simplesmente uma coleção dos casos de teste de caixa-branca que, em conjunto, exercitam todas as linhas de
código da aplicação pelo menos uma vez. O teste de caminho é um agrupamento de testes de cobertura que assegura que não
somente todas as linhas de código foram testadas, como também foram testados todos os caminhos lógicos. A principal
diferença ocorre quando existe um método com mais de um grupo de declarações CASE ou IFs aninhados: para determinar a
quantidade de casos de teste com testes de cobertura, é necessário calcular a quantidade máxima de trajetos para cada
variação das declarações CASE e IFs aninhados, e com testes de caminho, multiplicar pela quantidade de caminhos
lógicos.
Testes de Unidade e de Integração
O teste de unidade é o teste de um item, tal como uma operação isoladamente. Por exemplo, os testes definidos acima
para a função differenceInDays() são todos testes de unidade. O teste de integração, de outra forma, é o teste de uma
coleção de itens para validar que eles trabalham juntos. No caso da biblioteca/classe de dados, será que todas as
funções trabalham juntas? Talvez a função differenceInDays() tenha um efeito colateral que faça com que a função
dayOfWeek() falhe se a differenceInDays() for chamada primeiramente. O teste de integração procura por problemas como
este.
Testes Exploratórios
Os testes exploratórios são uma abordagem de testes que enfatiza as habilidades do testador em tomar decisões
sobre o que será testado durante a execução do teste ao invés de seguir um roteiro previamente planejado. No entanto, o
conceito não é novo. Glenford Myers em 1979, no seu livro chamado "The Art of Software Testing", já preconizava
conceitos embrionários de testes de natureza exploratória, quando discutia técnicas de testes de suposição de erros (do
inglês: "Error Guessing Testing").
Técnicas de Teste Orientadas a Objeto
Ao testar os sistemas construídos com a tecnologia de objeto é importante entender que o código fonte está composto de
diversas construções tais como métodos (operações), classes e herança. Conseqüentemente é necessário ter técnicas de
teste que reflitam o fato da existência dessas construções. Estas técnicas são:
-
Teste de Método
-
Teste de Classe
-
Teste de Integração de Classe
-
Teste de Regressão de Herança
Teste de Método
O teste de método é o ato de assegurar-se que os métodos(operações) trabalhem de acordo como o que foi definido. Nos
testes processuais isto seria chamado de teste de função ou procedimento. Os testes de método devem:
-
Assegurar que os métodos get e set funcionam como desejado
-
Assegurar que cada método retorne valores apropriados, incluindo mensagens de erro e exceções
-
Validar os parâmetros que estão sendo passados para um método
-
Assegurar que um método faça o que ele deve fazer
A vantagem do teste de método é que ele assegura que os métodos funcionam perfeitamente de forma isolada, entretanto
ele não ajuda a encontrar efeitos colaterais indesejados.
Teste de Classe
A principal finalidade do teste de classe é testar as classes isoladamente, sendo a combinação dos testes tradicionais
de unidade e de integração. É um teste de unidade porque você está testando a classe e suas instancias como unidades
isoladas, mas também é um teste de integração porque você precisa verifica se os métodos e os atributos da classe
funcionam em conjunto. Uma condição que deve ser assumida quando os "testes de classe" forem escritos é que todas as
outras classes do sistema funcionam perfeitamente. Os testes de classe devem:
-
Validar que os atributos de um objeto são corretamente inicializados
-
Validar as invariantes da classe
As principais vantagens do teste de classe são que ele valida se os métodos (operações) e os atributos (propriedades)
de uma classe estão funcionando em conjunto e se a classe funciona isoladamente. Entretanto, ele não garante que uma
classe funcione bem com o resto do sistema.
Teste de Integração de Classe
Também conhecido como teste de componente, esta técnica verifica se as classes do sistema, ou um componente do sistema,
trabalham corretamente em conjunto. Os relacionamentos entre as classes podem ser usados para orientar o
desenvolvimento de casos de teste de integração de classe. Os testes de integração de classe devem:
-
Validar que os objetos fazem o que os outros objetos esperam deles
-
Validar que os valores de retorno estão sendo gerados apropriadamente
-
Validar que as exceções e os erros estão sendo processados apropriadamente
A técnica ajuda a validar que todas as classes dentro de um componente, ou um sistema, trabalham em conjunto.
Entretanto, pode ser difícil definir e desenvolver os casos de teste para executar completamente este nível de teste.
Teste de Regressão de Herança
Este teste é o ato de executar casos de teste definidos para uma superclasse na instancia da subclasse porque os erros não
foram gerados pela subclasse. Os métodos novos são adicionados e os métodos existentes podem ser redefinidos pelas
subclasses, métodos que acessam e alteram os valores dos atributos definidos na superclasse. É possível que uma subclasse
mude o valor dos atributos de uma forma nunca desejada pela superclasse, ou pelo menos nunca esperada. O ponto principal é
que se deve executar o conjunto de testes das superclasses ao testar uma subclasse.
|