• Nenhum resultado encontrado

Para que se garanta o desenvolvimento de softwares de qualidade, uma gama de precau- ções e uma série de atividades são realizadas desde a etapa de levantamento de requisitos até a entrega do produto final ao cliente. Uma dessas atividades é o teste de software, cuja meta é controlar a qualidade pela descoberta de erros (MYERS; SANDLER; BADGETT,2011).

De modo particular, o termo erro tende a ser comumente usado de maneira flexível, significando defeito, erro ou falha (DELAMARO; MALDONADO; JINO,2007). Entretanto,

Figura 3 – Cenário da atividade de teste (DELAMARO; MALDONADO; JINO,2007)

a literatura especializada e o padrão IEEE 610.12 (IEEE,1990) estabelecem semânticas bem definidas para cada um desses termos, os quais podem ser descritos como:

• engano (mistake): ação humana que gera um defeito;

• defeito (fault): passo, processo ou definição de dados incorretos em um programa; • erro (error): produção de um estado inconsistente ou inesperado durante a execução do

programa e que pode ser ocasionado pela existência de um defeito;

• falha (failure): corresponde à geração de uma saída que difere do resultado esperado sendo oriunda da propagação de um erro previamente existente.

A atividade de teste, como vista na Figura3, apresenta um cenário típico. Neste cenário um programaP possui um domínio de entrada, D(P), que corresponde ao conjunto de todos os valores possíveis para serem executados emP. Um elemento qualquer do domínio de entrada do programaP é denominado dado de teste. O par formado pelo dado de teste e o resultado esperado da execução dele sobre o programaP é chamado de caso de teste e o conjunto de todos os casos de teste utilizados na atividade de teste de um programa é conhecido como conjunto de

teste (DELAMARO; MALDONADO; JINO,2007).

Duas situações podem ocorrer quando se defineT como um conjunto de teste, executa-se T sobre o programa P e observa-se os resultados de saída. A primeira é a obtenção de resultados que coincidem com a saída esperada, caracterizando que nenhum erro foi observado, a segunda é obter uma ou mais saídas diferentes daquela esperada, evidenciando a presença de um ou mais defeitos no programa. O oráculo, usualmente o testador, é o elemento que determina se a saída obtida está em conformidade com o esperado ou não. Ele faz esta decisão baseado nas especificações do programa S(P) ou em qualquer outro documento que descreva o seu

comportamento (DELAMARO; MALDONADO; JINO,2007).

É importante ressaltar que a garantia de um programa estar livre de defeitos só pode ser dada se ele for testado com todos os elementos de seu domínio de entradaD(P). Porém,

3.2. Terminologia e Conceitos Básicos 27

a maior parte dos programas possuem domínios de entrada muito grandes ou infinitos, o que inviabiliza tal abordagem. A alternativa é utilizar regras conhecidas como critérios de teste, a fim de categorizar os dados de teste em subconjuntos deD(P) que possuam maior probabilidade de revelar defeitos. Um subconjunto ou subdomínio se caracteriza por generalizar casos de teste que possuem comportamentos semelhantes. Deste modo, um único elemento do domínio pode representar vários outros e a atividade de teste pode ser realizada com um conjuntoT reduzido, se comparado aD(P). Entretanto, há sempre o risco de se negligenciar algum caso de teste que poderia revelar um defeito do programa, o que direciona o objetivo da atividade de teste, não para a demonstração da corretude do programa, mas para a revelação de defeitos caso eles existam (DELAMARO; MALDONADO; JINO,2007).

A qualidade do software final tem uma relação direta com a quantidade de defeitos encontrados durante a fase de teste. De maneira equivocada pode-se pensar que a estratégia ideal seria gastar mais tempo na atividade de teste tentando descobrir a maior quantidade possível de defeitos. Entretanto, a atividade de teste é uma das mais caras e complexas no processo de desenvolvimento de software e prolongar essa fase implicaria em custos muito altos para o produto final (MYERS; SANDLER; BADGETT,2011).

A complexidade observada na atividade de teste faz com que a mesma seja dividida em três fases distintas (PRESSMAN,2011):

• teste de unidade: foca nas menores unidades de um sistema, como funções, métodos ou classes, e busca identificar erros relacionados a algoritmos ou estruturas de dados incorretos, ou ainda simples erros de programação. O teste de cada unidade realizado em separado possibilita que o mesmo seja feito à medida que as unidades vão sendo implementadas, não havendo a necessidade de ter à disposição o sistema em questão completamente finalizado.

• teste de integração: ao término do teste de unidade, os módulos podem ser gradativamente integrados para a construção do sistema. Conforme as partes do software são agrupadas é verificada se a integração entre elas funciona da maneira apropriada. Quando fica demonstrado que o módulo adicionado à estrutura do programa realiza corretamente suas tarefas, juntamente com as demais partes, outro módulo é acoplado à estrutura e o teste de integração continua. Este processo se repete até que o sistema esteja totalmente integrado e testado.

• teste de sistema: com a junção de todos os módulos têm-se o sistema completo. Nessa situação é possível efetuar o teste de sistema que tem como propósito a verificação da correta realização das funcionalidades especificadas para o produto final. Também são verificados requisitos não funcionais como desempenho, usabilidade e segurança. É comum que exista uma equipe independente para a realização desta fase de teste.

As técnicas e critérios de teste auxiliam os testadores a conduzirem a atividade de teste de modo mais eficaz, contribuindo para a garantia da qualidade do software testado e ao mesmo tempo fornecendo uma visão do custo para o desenvolvimento desta atividade (DELAMARO; MALDONADO; JINO,2007). As técnicas mais comuns que podem ser adotadas nesse sentido são:

• técnica funcional: neste tipo de teste a estrutura do programa não é considerada, ou seja, ela é vista como uma caixa-preta e os casos de teste são projetados a partir de seus requisitos funcionais. No teste de caixa-preta o testador conhece as entradas que podem ser dadas ao sistema e quais saídas são esperadas, logo a análise da presença de um defeito envolve a comparação das saídas obtidas ao executar os valores de entrada com as saídas esperadas (AGARWAL; TAYAL; GUPTA,2010).

• técnica baseada em defeitos: esta técnica visa verificar se o conjunto de casos de teste é bom o suficiente para testar um programa. Para tanto, são inseridos defeitos no código fonte, gerando-se códigos mutantes que são executados com os casos de teste. Se o conjunto de casos de teste é bom o suficiente, os mutantes gerados irão apresentar uma saída diferente do esperado, demonstrando que aquela parte do código com o defeito inserido foi testada. Há também a possibilidade da saída não ser diferente da esperada e mesmo assim aquela região código ter sido exercitada. Neste caso o mutante é dito ser equivalente ao original. Cabe ao testador obter informações dos defeitos mais comuns, saber como tais defeitos se refletem em falhas e então demonstrar a ausência de tais falhas (PERRY,2006).

• técnica estrutural: nesta abordagem o testador deve conhecer os aspectos estruturais e de implementação do software (teste de caixa-branca). O testador pode gerar casos de teste que exercitam os componentes elementares do código como: desvios condicionais, laços, pares de definições e usos de variáveis (AGARWAL; TAYAL,2007). Dada a importância desta técnica para este trabalho, ela será detalhada a seguir.