Técnicas de Teste Estrutural Teste de Fluxo de Controle Teste funcional vs. Teste Estrutural Teste funcional Usa como entrada a especificação funcional. Procura verificar se todas as funcionalidades previstas foram implementadas e se respondem como esperado. Incapaz de verificar funcionalidades ou comportamentos extras não especificados. Teste estrutural Usa como ponto de entrada o código fonte. Mais propenso a automação. Procura exercitar o maior número possível de possibilidades de execução do programa, de acordo com algum critério pré-definido No teste estrutural a especificação funcional é usada apenas como oráculo. Teste estrutural e teste funcional são complementares: Teste estrutural ajuda a detectar comportamentos que foram implementados mas não especificados. Teste funcional detecta comportamentos que foram especificados, mas não implementados e que o teste estrutural jamais testaria. Análise estática: Oferece parâmetros de qualidade do código fonte. Será visto mais adiante. Análise dinâmica: Exercita o código fonte em busca de erros. Cada técnica define um critério de cobertura: fluxo de controle fluxo de dados análise de mutantes Por serem baseadas na análise do código fonte, as técnicas estruturais dependem da linguagem de programação usada na verdade, do paradigma de programação utilizado. Inicialmente serão discutidas as técnicas de teste estrutural para programas imperativos. Mais adiante comentaremos o que muda quando se consideram outros paradigmas.
Grafo de Fluxo de Controle Duas categorias principais: Teste de fluxo de controle. Teste de fluxo de dados. Compartilham uma característica importante: baseiam-se no conceito de grafo de programa. Grafo de Fluxo de Programa: Grafo dirigido onde os nodos representam comandos e os arcos representam o fluxo de execução Existe um arco de um nodo i para um nodo j se e somente se j pode ser executado imediatamente após i Grafo de programa Grafo de Fluxo de Controle Quais comandos representar? Dependente do paradigma e da linguagem Em C/C++/Java não se representam as declarações de variáveis porque não correspondem a comandos executáveis. Todo grafo de programa tem um único nodo inicial e um nodo final.a = Integer.parseInt(args[0]);.b = Integer.parseInt(args[]);.while f(a){. if (f(b)){. a = b + ; 6. }else b = a + ;. } 8. c = a + b; Exemplo 6 Grafo de Programa Estruturas típicas de um grafo de programa: 8
Exercícios ) Desenhe o grafo de programa correspondente aos trechos de código a seguir:. public void ordena(int *vet, int pos){. int i=0;. while (i<pos-) {. int j=0;. while (j<pos-) { 6. if (vet[j] > vet[j+]){. int aux = vet[j]; 8. vet[j] = vet[j+];. vet[j+] = aux; 0. }. j = j++;. }. i = i++;. }. } Exercícios. String cab=""; int qtd;. for(int j=0;j<veiculo.qtdade_campos;j++). cab += Veiculo.ROTULOS[j]+"\t";. cab += "Imagem\t";. cab += "LogoRevenda\t"; 6. ps.println(cab);. for(int i=0;i<tam;i++){ 8. qtd = dis.readint();. aux = dis.readutf()+logorevenda+"\t"; 0. for(int j=0;j<qtd;j++). ps.println(aux);. String ni = dis.readutf();. int nbytes = dis.readint();. FileOutputStream ios= new FileOutputStream(ni);. for(int j=0;j<nbytes;j++) 6. ios.write(dis.read());. ios.close(); 8. }. dis.close(); fis.close(); ps.close();fos.close(); Exercícios. public static void verificaoucriadirveiculos(){. File f = new File(Revenda.getDirVeiculos());. if (f.exists()){. if (f.isdirectory()) return;. JOptionPane.showMessageDialog(null, 6. "Nao e possivel criar o diretorio veiculos"+. "\nremova o arquivo com este nome diretório "+ 8. "corrente");. System.exit(0); 0. } else {. f.mkdir();. File fimg = new File("NoVehicle.jpg");. JTextField cmps[]=new JTextField[Veiculo.QTD];. for(int i=0; i<veiculo.qtd;i++). cmps[i] = new JTextField(0); 6. campos[veiculo.placa].settext("novehicle");. Veiculo.save(cmps,fImg); 8. }. } Análise dinâmica Execução monitorada do programa Pode exigir instrumentação de código Abordagens: Análise de cobertura Teste de fluxo de controle Teste de fluxo de dados Análise de mutantes Análise dinâmica Análise dinâmica Análise de cobertura Dado um conjunto de execuções do programa, quanto do código fonte foi exercitado (coberto)? Geração de casos de teste Cria-se os casos de teste com o objetivo de atingir um determinado valor de cobertura Métricas: Fluxo de controle Fluxo de dados Grafo de Fluxo de Controle: Define uma relação entre o caso de teste e a parte do programa exercitada por ele. Um caso de teste: Corresponde a um caminho no grafo. Corresponde a uma execução completa: do nodo inicial até o final.
Teste de Fluxo de Controle Cada caso de teste corresponde a um caminho no grafo. Etapas: Construir o grafo de fluxo de programa. Determinar os caminhos factíveis. Selecionar um conjunto de caminhos factíveis para teste. Gerar as entradas e os resultados esperados para os casos de teste correspondentes. Exemplo. public Triangulo(String args[ ]){. int a,b,c;. String resp = null;. a = Integer.parseInt(args[0]);. b = integer.parseint(args[]); 6. c = integer.parseint(args[]);. if (a==b)&&(b==c) 6 8. resp = equilatero ;. if (((a==b)&&(b!=c)) ((b==c)&&(a!=b)) ((a==c)&&(c!=b)) 0. resp = isoceles ;. if ((a!=b)&&(b!=c)) 8. resp = escaleno ;. system.out.println( Tipo de triangulo: +resp);. } 0 Caminhos Sobre o Grafo Sobre o grafo do problema do triângulo podemos distinguir os seguintes caminhos:. --6----. --6--8---. --6---0--. --6--8--0--. --6----- 6. --6--8----. --6---0--- 8. --6--8--0--- Caminhos Sobre o Grafo Existem caminhos factíveis. Ex: caminhos,, e. Existem caminhos não factíveis. Ex: caminhos,6, e 8. A existência de caminhos não factíveis indica que talvez a lógica pudesse ser escrita de outra maneira. Teste de Fluxo de Controle Objetivos do teste de fluxo de controle: Exercitar um mínimo aceitável de caminhos factíveis. O que determina o que é um mínimo aceitável são as métricas de cobertura : Cobertura de comando Cobertura de decisão Cobertura de condição Cobertura de condição múltipla Cobertura de repetição Cobertura de Comando Definir casos de teste que executem todos os comandos pelo menos uma vez (passa por todos os nodos do grafo). Métrica: número de nodos cobertos. Fácil de satisfazer: Para o problema do triângulo bastam casos de teste (caminhos, e ) Não garante muita coisa: Não testa o caso em que a entrada não é um triângulo Não detectaria o seguinte bug: if (((a==b)&&(b!=c)) ((b==c)&&(a!=b))&&((a==c)&&(c!-b))...
Cobertura de comando: exemplo Cobertura de Decisão. a = Integer.parseInt(args[0]);. b = Integer.parseInt(args[]);. while (a < 0) {. if (b < 0) {. b = b + ; 6. }. a = a + ; 8. }. c = a + b; Os testes devem cobrir cada saída possível de um nodo que tenha uma condição. Métrica: número de arestas cobertas. Cobertura de condição implica em cobertura de comando, mas o inverso nem sempre é verdade. a b c (res. esperado) - - Cobertura de Decisão: Exemplo Cobertura de Condição. a = Integer.parseInt(args[0]);. b = Integer.parseInt(args[]);. while (a < 0) {. if (b < 0) {. b = b + ; 6. }. a = a + ; 8. }. c = a + b; a b c (res. esperado) Para cada condição deve-se cobrir os dois resultados possíveis. Ex: if ((A>0) or (B<0))... Exemplos de casos de teste: A = e B = 0 (A verdade e B Falso) A = 0 e B = (A falso e B verdade) - - - - Cobertura de Condição múltipla Cobertura de Condição Múltipla: Exemplo Necessidade de verificar melhor os nodos que tem condições múltiplas. Cria-se uma tabela verdade para cada nodo desses e gera-se um caso de teste para cada entrada da tabela. Resulta em diferentes entradas para alguns caminhos que vão se repetir.. a = Integer.parseInt(args[0]);. b = Integer.parseInt(args[]);. while ((a < 0) or (b < -)) {. if (b < 0) {. b = b + ; 6. }. a = a + ; 8. }. c = a + b; a b c (res. esp) - (V) - (F) 0 (F) - (F) - -(V) - (V) - 0 (F) - (V) 0
Cobertura de Repetição Gerar casos de teste de maneira que cada repetição seja exercitada pelo menos k vezes. Deve ser usada em combinação com a cobertura de condição. Pode-se aplicar técnicas de valor limite nos laços. 6