Resumo
Este é o primeiro de uma série de artigos onde é realizado um estudo da atividade de testes e sua aplicação durante o ciclo de vida do software como uma ferramenta para aumentar a qualidade do produto e otimizar a utilização dos recursos empregados no processo de teste de software.
Neste artigo serão considerados os princípios, objetivos e classificação dos testes, com a finalidade de fundamentar, através do estudo da teoria de testes, os assuntos que serão abordados posteriormente.
Palavras-chave: acurácia, ciclo de vida, confia-bilidade, desenvolvimento, qualidade, revisão, testes.
1. Introdução
A fase de testes ocupa, normalmente, 40% do tempo planejado para um projeto e um erro descoberto tardiamente em um sistema provoca um acréscimo de 60% nos custos do projeto[6]. Nenhum programador ou analista, por mais experiente que seja, está imune a falhas de codificação e projeto.
Estes são alguns dos motivos que têm feito com que a atividade de teste de software tenha se tornado um dos itens mais estudados no contexto de aprimoramento da qualidade de software.
Apesar de parecer simples à primeira vista, a atividade de teste exige um bom planejamento e controle durante a execução para ser bem sucedida. Conhecer os aspectos da atividade de testes como as limitações, objetivos, formas de criação de massa de testes e de aplicação de técnicas de teste de acordo com o momento do projeto, facilita o trabalho de planejamento e controle.
E nesta atividade o melhor é investir ao longo de todo o processo de forma equilibrada para evitar a necessidade de um esforço muito grande e menos efetivo ao final do desenvolvimento.
2. Qualidade de software e testes
A história da garantia de qualidade no desenvolvimento de software tem paralelo com a história da qualidade no processo de manufatura de hardware. Na fase inicial da computação (décadas de 50 e 60), a qualidade era uma responsabilidade exclusiva do programador, sendo que padrões de garantia de qualidade para o software foram introduzidos no desenvolvimento de software sob contrato militar durante a década de 70. A partir daí espalharam-se rapidamente para o desenvolvimento comercial de software [6].
Neste artigo entende-se que a atividade de teste deve ser usada em todas as etapas do processo de desenvolvimento de software e que, ao invés de representar uma última revisão, seja utilizada como "milestone" entre todas as fases do projeto, pois os erros, como afirma Deutsch, podem estar já nas primeiras fases do projeto, e quanto mais tarde forem descobertos, maior impacto causarão. Do ponto de vista financeiro, um estudo realizado pela IBM [3] mostra que enquanto um erro encontrado e corrigido durante a fase de projeto custa 1 unidade monetária, se o mesmo for encontrado durante a atividade de testes de código, custará 15 unidades. Após o lançamento do produto, a correção do mesmo erro custará entre 60 e 100 unidades monetárias.
3. Teste de software
Este capítulo abordará os aspectos fundamentais da atividade de teste, através de revisão bibliográfica. Os princípios, limitações e objetivos da atividade de teste serão apresentados nos itens que se seguem. Veremos também as características principais de cada classe de testes.
3.1 Considerações
Programar é um exercício para resolução de problemas[1], uma vez que um programa é um modelo que pretende representar a solução para um problema do mundo real escrito em linguagem de máquina. Por ter tal característica, faz parte do processo determinar se esta representação da solução é válida e correta.
Se considerarmos que todas as definições produzidas ao longo do ciclo de vida do software podem ser representadas em linguagem formal, pode-se perceber que todos estes produtos podem ser testados de forma a validar e medir sua qualidade.
Os objetos testados são todos aqueles que aparecem durante o processo de desenvolvimento de software. Isto inclui módulos de código, especificações de requisitos e projeto, estruturas de dados, e qualquer outro objeto que surja durante o processo de desenvolvimento e implementação de software.
Portanto, o termo programa designa qualquer objeto que possa ser conceitualmente ou realmente executado. É este o significado utilizado no presente artigo.
Programas podem ser vistos como uma função, uma vez que descrevem o relacionamento dos elementos de entrada com os elementos de saída. O processo de testes é utilizado para verificar se o programa realiza suas atribuições de forma confiável, ou seja, se para uma determinada entrada, a saída obtida corresponde à esperada.
Uma completa validação do programa em qualquer estágio do ciclo de vida pode ser obtida através da execução do processo de teste para cada valor de entrada possível. Se cada instância for bem-sucedida, o programa foi verificado; senão, um erro foi encontrado. Este método é conhecido com o teste exaustivo e é a única técnica de testes que garantiria a validade do programa. Lamentavelmente esta técnica não é viável. Na maior parte dos casos, o domínio da função (conjunto de dados de entrada possíveis) é infinito, ou quando finito, grande o bastante para fazer o número de testes requeridos inviável.
Visando reduzir o número potencialmente infinito de testes do processo de testes exaustivos para um número possível, deve-se encontrar um critério para selecionar elementos representativos do domínio da função. Estes critérios devem refletir tanto a descrição funcional quanto a estrutura do programa.
O subconjunto de elementos selecionado para ser usado no processo de testes é chamado de conjunto de dados de teste ou ‘test set". No entanto, o problema é encontrar um test set adequado, que seja grande o suficiente para englobar todos os valores válidos do domínio e suficientemente pequeno para que se possa testar elementos de cada tipo de entrada do conjunto.
A principal técnica de teste continua sendo executar o software usando uma massa de testes que contenha dados representativos como entradas para o sistema ou módulo e comparar a saída gerada com a esperada. Diferenças representam falhas que devem ser corrigidas.
3.2 Princípios do teste de software
Adaptando a citação de Glen Myers[5] , pode-se dizer que a atividade de teste de software é o processo de revisar especificações, projetos e programas com a intenção de descobrir erros. Alguns dos itens que são comuns a todos autores e pesquisadores do assunto "teste de software" e que descrevem os fundamentos e princípios desta atividade, estão relacionados abaixo:
-
Conforme a Lei de Pareto, 80% dos erros podem ser localizados em 20% do projeto, geralmente nos módulos principais do sistema;
-
A atividade de teste não prova a ausência de erros, apenas a existência dos mesmos;
-
Bons casos de teste são aqueles que encontram falhas no sistema até então não descobertas;
-
Bons casos de teste são projetados levando em conta os requisitos do projeto;
-
Um critério que pode ser utilizado para determinação do esforço a ser gasto na atividade de teste de software é verificar qual o grau de severidade das conseqüências advindas do seu mau funcionamento;
-
A probabilidade de encontrar um erro numa determinada parte do sistema é proporcional ao número de erros já encontrados nesta parte;
-
A maior parte dos autores concorda que os programas devem, preferencialmente, ser testados por pessoas não envolvidas no processo de desenvolvimento, por uma equipe independente. Pode haver também a interação dos desenvolvedores com a equipe independente, justificando as decisões tomadas durante o projeto. Esta abordagem ajuda na revisão do projeto.
3.3 Objetivos do teste
O objetivo da atividade de teste pode ser entendido da seguinte forma:
-
no início de cada fase verificar se esta etapa do projeto reflete exatamente os requisitos e definições da fase imediatamente anterior, para com isso garantir que o produto encomendado e o gerado pela atividade de desenvolvimento do software sejam os mesmos, através dos diferentes níveis de refinamento do projeto;
-
verificar se não existem erros de lógica no projeto e código, no fluxo de dados, no entendimento de requisitos, de codificação, tipográficos ou de interface em todas as fases do projeto;
-
identificar e interferir na presença do erro, iniciando-se a depuração, sendo que quanto antes for descoberta a falha, menos custoso será para adequá-la;
-
ter em mente que, uma vez que errar é humano e atividade de desenvolvimento de software é um exercício bastante complexo, os erros existem e devem ser descobertos. Portanto, o sucesso em um teste consiste em descobrir os erros e corrigi-los.
3.4 Classificação dos testes
Podemos classificar os métodos de teste de acordo com suas características básicas. Existem testes estáticos e dinâmicos, testes estruturais e funcionais, testes de unidade e integração, testes de validação, testes alfa e beta, testes de recuperação, testes de segurança, testes de estresse e testes de desempenho. Pode-se descrever resumidamente cada um destes grupos da seguinte forma:
-
Testes estáticos ou testes humanos: não são feitos através da execução real do programa, mas sim através da execução conceitual do mesmo. Métodos classificados como estáticos são os de walkthrough, inspeções e peer rating. São utilizados principalmente para validar as primeiras etapas do projeto como: de elicitação de requisitos, projeto preliminar e projeto detalhado, podendo ser usados para validar o código também.
-
Testes dinâmicos: Requerem que o programa seja executado, e por isso seguem o modelo tradicional de teste de programa, no qual o programa é executado com alguns casos de teste e os resultados da execução são examinados para verificar se o programa operou de acordo com o esperado. São usados principalmente na validação do código em módulos e na integração geral do sistema.
-
Testes funcionais: Uma vez que testes exaustivos não são viáveis, características do domínio de entrada são examinadas para que se tente descobrir formas de derivar um conjunto de dados de teste representativo que consiga exercitar completamente a estrutura do sistema. Os dados de teste precisam ser derivados de uma análise dos requisitos funcionais e incluir elementos representativos de todas as variáveis do domínio. Este conjunto deve incluir tanto dados de entrada válidos quanto inválidos. Geralmente, os dados no conjunto de dados de teste podem ser classificados em três classes: de fronteira, não de fronteira e especiais. Exemplificando: Se X for um número e se tivermos X tal que, a < X < b, então X deve ser testados para os valores a + 1 e b – 1 da classe válida, para os valores a e b da classe inválida e para valores especiais tais como zero, vazio, ou caracteres alfabéticos. São considerados métodos de testes funcionais ou de caixa preta: o particionamento de equivalência, a análise de valor limite, a técnica de grafo de causa-efeito e os testes de comparação.
-
Testes estruturais: Diferentemente dos testes funcionais, que se preocupam com a função que o programa desempenha sem se preocupar com a maneira como a função foi implementada, o teste estrutural enfoca a implementação e a estrutura da função. Embora geralmente usado durante a fase de codificação, testes estruturais podem ser usados em todas as fase do ciclo de vida do software nas quais o software é representado formalmente. A intenção do teste estrutural é encontrar dados de teste que terão cobertura suficiente de todas as estruturas presentes na representação formal.
-
Testes de unidade: Concentra-se no esforço de verificação da menor unidade de projeto de software: o módulo. Através do uso da descrição do projeto detalhado como guia, caminhos de controle importantes são testados para descobrir erros dentro das fronteiras do módulo. Este teste baseia-se sempre na caixa branca, e esse passo pode ser realizado em paralelo para múltiplos módulos. Nos testes de unidade são verificados: a interface com o módulo, a estrutura de dados local, as condições de limite, todos os caminhos independentes através da estrutura de controle e todos os caminhos de tratamento de erros. Um caminho independente é qualquer caminho através do programa que introduza um novo conjunto de instruções de processamento ou uma condição nova. O procedimento comumente utilizado para teste de unidade envolve o desenvolvimento de um software driver e/ou stub para cada unidade de teste. O driver é um programa principal que aceita dados de caso de teste, passa tais dados para o módulo a ser testado e imprime os dados relevantes. Os stubs servem para substituir módulos que estejam subordinados ao módulo a ser testado. Um stub ou programa simulado usa a interface do módulo subordinado, pode fazer manipulação de dados mínima, imprime verificação de entrada e retorna.
-
Testes de integração: O objetivo é, a partir dos módulos testados no nível de unidade, construir a estrutura do programa que foi determinada pelo projeto de forma sistemática, testando também a interface dos módulos. A integração pode ser incremental ou não-incremental. A integração não-incremental, executada através da abordagem do big-bang combinando-se antecipadamente todos os módulos, não costuma ser eficaz, dada a amplitude de um teste do programa como um todo. Torna-se difícil isolar um erro e, quando esses são corrigidos, novos erros são gerados na estrutura do programa. Já a integração incremental é mais eficiente, podendo seguir a abordagem top-down ou a bottom-up. Discussões sobre as vantagens e desvantagens relativas do teste de integração top-down versus bottom-up podem ser vistas em [2].
-
Testes de validação: O teste de validação pode ser considerado bem sucedido quando o software funciona da maneira esperada pelo cliente. Ou seja, verifica-se se o produto certo foi construído, seguindo a especificação de requisitos do software. A validação do software, na fase de testes, é realizada por meio de uma série de testes de caixa preta que demonstram a conformidade com os requisitos.
-
Testes alfa e beta: São os testes de aceitação, feitos pelo usuário, que dificilmente opera o sistema da forma prevista, e visam descobrir erros cumulativos que poderiam deteriorar o sistema no decorrer do tempo. O teste alfa é executado por um cliente nas instalações do desenvolvedor, sendo acompanhado pelo desenvolvedor, que registra os problemas encontrados no uso. O ambiente é controlado. Já o teste beta é realizado em uma ou mais instalações do cliente pelo usuário final do software. Geralmente o desenvolvedor não está presente. Assim, o teste beta é uma aplicação real do software, sem que haja controle por parte do desenvolvedor. Os problemas são registrados pelo usuário e repassados regularmente ao desenvolvedor, que corrige o software antes de lançar o produto para venda.
-
Teste de segurança: O teste de segurança tenta verificar se todos os mecanismos de proteção embutidos em um sistema o protegerão de acessos indevidos. Todas as formas de ataque devem ser simuladas. A finalidade é dificultar o acesso indevido de forma que seja mais interessante e barato obter a informação de forma correta e legal.
-
Testes de estresse: O teste de estresse é feito para confrontar o sistema com situações anormais. O teste de estresse executa o sistema de forma que exige recursos em quantidade, freqüência ou volume anormais.
-
Teste de desempenho: O teste de desempenho é idealizado para testar o desempenho do software quando executado dentro do contexto de um sistema integrado. Algumas vezes os testes de desempenho são combinados com os de estresse e podem ser executados durante todo o processo de desenvolvimento.
4. Conclusões
Embora seja difícil medir e definir um software como sendo de boa qualidade, é fácil identificar um software de má qualidade. Os erros freqüentes, o mau funcionamento, ou a inadequação aos requisitos são sempre notados.
A atividade de testes, quando bem realizada, torna-se uma forma de avaliar e agregar qualidade ao produto, reduzir custos e retrabalho, melhorando a imagem da empresa e ampliando sua capacidade competitiva.
Por estas razões, esforços de planejamento e controle da execução de testes devem ser feitos a partir das etapas iniciais, para que a atividade de testes esteja de acordo com a qualidade do software que se pretende gerar.
5. Próximos passos
Nos próximos artigos serão apresentadas as técnicas de teste existentes, através de revisão bibliográfica. Com base nestas técnicas e tendo em vista as necessidades da atividade de teste para cada etapa do processo de desenvolvimento, será proposta a aplicação das técnicas ou estratégia que sejam as mais indicadas para as diferentes fases do projeto.
Serão vistas técnicas funcionais, estruturais, estáticas e dinâmicas, apresentando-se suas características e oportunidades de aplicação. Para cada fase serão sugeridas alternativas da utilização das técnicas e listas de revisão, com o objetivo de facilitar o planejamento da atividade de testes.
REFERÊNCIAS BIBLIOGRÁFICAS
1. ADRION, W. Richards; BRANSTAD, Martha A.; CHERNIAVISKY, John C. Validation, verification, and testing of computer software. ACM Computing Surveys, New York, v. 14, n. 2, p. 159-192, jun. 1982.
2. DEUTSCH, M. Verification and validation. In: PRESSMAN, Roger. Engenharia de software. São Paulo : Makron Books, 1995. p. 780-781.
3. IBM Systems Sciences Institute. Implementing software inspections. New York: IBM Corporation, 1981.
4. IEEE. Software engineering standarts. New York : IEEE Computer Society Press, 1989.
5. MYERS, Glen. The art of software testing. New York : J. Wiley, 1979.
6. PRESSMAN, Roger. Engenharia de software. São Paulo : Makron Books, 1995.
|