Teste de Fluxo de Dados
Técnicas de Teste Estrutural
Teste de Fluxo de Dados
Análise de Mutantes
• Gera casos de teste analisando o
comportamento das variáveis do
programa.
• Procura cobrir caminhos do grafo que
correspondam a diferentes
combinações de estados e usos das
variáveis.
• Conceitos chave:
▪ Definição de uma variável
▪ Uso de uma variável
Teste de Fluxo de Dados
• Um nodo n em G(P) é um nodo
definição de uma variável v em V se v
recebe um valor no comando
representado por n e denotamos esse
fato por def(v,n).
• Um nodo n em G(P) é um nodo uso de
uma variável v em V se o valor de v é
usado (referenciado) no comando
representado por n e denotamos esse
fato por use(v,n).
Teste de Fluxo de Dados
• São nodos de definição:
▪ Comandos de atribuição.
▪ Comandos de entrada.
▪ Parâmetros por referência que são
alterados no corpo da rotina.
• São nodos de uso:
▪ Comandos condicionais.
▪ Comandos de saída.
▪ Parâmetros por valor ou parâmetros por
referência que não são alterados no corpo
da rotina.
• Nodos híbridos:
▪ i := i + 1;
Teste de fluxo de Dados
• Classificação dos nodos uso:
▪ Uso-P: um nodo uso n em relação a
uma variável v é um nodo uso-P (uso
em predicado) se o comando
correspondente a n é uma condição (o
nodo é origem de mais de uma aresta).
▪ Uso-C: um nodo uso n em relação a
uma variável v é um nodo uso-C (uso
em computação) se o comando
correspondente a n não é uma
condição (ou seja, se o nodo é origem
de no máximo uma aresta).
Teste de Fluxo de Dados
• O teste de fluxo de dados exige a
cobertura de caminhos que podem
não ser exigidos pelo teste de
fluxo de controle.
2
• No exemplo ao lado, para
satisfazer cobertura de condição
basta cobrir os caminhos:
1,2,4,5,7 e 1,3,4,6,7
5
• Se 2 for um nodo definição para v
e 6 um nodo uso para v, este
caminho não será testado.
1
3
4
6
7
1
Teste de Fluxo de Dados
• No teste de fluxo de controle
consideram-se apenas caminhos
completos (do nodo inicial ao final).
No teste de fluxo de dados os subcaminhos se tornam importantes.
• Consideram-se os caminhos que
ligam um nodo definição a um nodo
uso.
Teste de Fluxo de Dados
• Caminhos-d-u que não são caminhos
d-c são caminhos onde a variável é
inicializada duas vezes antes de ser
usada.
• Nodos uso que não pertencem a
nenhum caminho-d-u correspondem a
variáveis usadas sem terem sido
previamente inicializadas.
Teste de Fluxo de Dados
• Tipos de sub-caminhos:
▪ Caminho-d-u: (definition-use) em relação
a uma variável v em um grafo de
programa G(P) é um sub-caminho de G
tal que o nodo inicial m do caminho é um
nodo definição de v e o nodo final n é um
nodo uso de v (use(v,n)).
▪ Caminho-d-c: (definition-clear) em
relação a uma variável v em um grafo de
programa G(P) é um caminho-d-u tal que
não existe outro nodo definição de v no
caminho além do nodo inicial m.
Critérios de cobertura
• Critérios de cobertura de fluxo de
dados:
▪ Cobertura de todas as definições (alldefs): para cada nodo definição de
cada variável de P, cobre-se pelo
menos um caminho-d-c para algum
nodo uso.
• Nodos definição que não pertencem a
nenhum caminho-d-c correspondem a
variáveis inicializadas mas não usadas.
▪ Cobertura de todos os usos (all-uses):
para cada variável v de P cobre-se pelo
menos um caminho-d-c de cada nodo
definição de v para cada nodo uso de v
e cada nodo sucessor deste nodo uso.
Critérios de cobertura
Critérios de cobertura
▪ Cobertura de todos os usos-P (all-Puses): para cada variável v de P cobrese pelo menos um caminho-d-c de cada
nodo definição de v para cada nodo
uso-P de v e cada nodo sucessor deste
nodo uso.
▪ Cobertura de todos os usos-P/alguns
usos C (all-p-uses.some-C-uses): para
cada variável v de P cobre-se pelo
menos um caminho-d-c de cada nodo
definição de v para cada nodo uso-P de
v ou para pelo menos um uso-C, caso v
não tenha nenhum uso-P.
▪ Cobertura de todos usos-C (all-Cuses): para cada variável v de P cobrese pelo menos um caminho-d-c de cada
nodo definição de v para cada nodo de
uso-C de v e cada nodo sucessor deste
nodo uso.
2
Critérios de cobertura
Critérios de cobertura
▪ Cobertura de todos os usos-C/alguns
usos-P (all-C-uses.some-P-uses): para
cada variável v de P cobre-se pelo
menos um caminho-d-c de cada nodo
definição de v para cada nodo uso-C de
v ou pelo menos um uso-P, caso v não
tenha nenhum uso-C.
▪ Cobertura de todos os caminhos-d-u
(all-DU-paths): para cada variável v de
P cobre-se todos os caminhos-d-c
acíclicos ou que percorrem cada ciclo
no máximo uma vez, de cada nodo
definição de v para cada nodo uso de v
e cada nodo sucessor deste nodo uso.
• Cobertura all-uses:
Exemplo
1.
2.
typedef struct { char num, code[ 20 ]; } node;
static node tab[] = {
{'0', "aeiouhywAEIOUHYW"},
{'1', "bpfvBPFV"},
{'2', "cskgjqxzCSKGJQXZ"},
{'3', "dtDT"},{'4', "lL"},
{'5', "mnMN"}, {'6', "rR"}};
3. char groupfor(char t){
4.
int i = 0;
5.
while(i < 7){
6.
if (strchr(tab[i].code,t))
7.
return tab[ i ].num;
8.
i++;
9.
}
10.
printf("Not found: %c\n",t);
11.
return '0';
12. }
• Conjunto de variáveis de P = {tab,t,i}
• Nodos definição e uso:
variável
definição
Uso
tab
2
6,7
t
3
6,10
i
4, 8
5,6,7,8
• Cobertura all-defs:
Variável
caminho
▪ Hipótese do programador competente: a
maioria dos erros cometidos em programas
são relativos a detalhes (operador errado,
confusão com nome de variáveis etc)
▪ Hipótese do acoplamento: um conjunto de
casos de teste capaz de detectar erros
relativos a detalhes será capaz de detectar
erros mais complexos.
caminho
tab
2-3-4-5-6
tab
2-3-4-5-6-8
tab
2-3-4-5-6-7
tab
2-3-4-5-6-7-12
t
3-4-5-6
t
3-4-5-6-7
t
3-4-5-6-8
t
3-4-5-10
t
3-4-5-10-11
i
4-5
i
4-5-6
i
4-5-10
i
4-5-6-7
4-5-6-8
tab
2-3-4-5-6
i
t
3-4-5-6
i
4-5-6-7-12
i
4-5
i
8-5
i
8-5
i
8-5-6
i
8-5-10
i
8-5-6-7
i
8-5-6-8
i
8-5-6-7-12
Análise de Mutantes
• Também conhecida como teste baseado
em erros.
• É uma forma de avaliar a qualidade de um
conjunto de casos de teste.
• Baseia-se na análise do código fonte.
• Hipóteses:
Variável
Análise de Mutantes
• Idéia básica:
▪ a partir do programa original geram-se
programas com pequenas falhas e
criam-se casos de teste capaz de
detecta-las.
▪ Para verificar se um teste detecta a
existência de falhas introduzidas,
compara-se o comportamento do
mutante com o do programa original.
3
Análise de mutantes
Análise de Mutantes
•
• Conceitos:
Processo de teste de mutação:
▪
▪ Mutante: dado um programa P, um
mutante de P é um programa P’
sintaticamente correto obtido a partir
de uma alteração no código fonte
definida por um operador préestabelecido chamado operador de
mutação.
▪ Morte de mutantes: Um teste T mata
um mutante P’ se o resultado da
execução de P’ para T é diferente do
resultado da execução de P para T.
▪
▪
▪
▪
Iniciar com o programa P a ser testado e um
conjunto de testes T (possivelmente vazio);
Gerar um conjunto M de mutantes de P,
através de operadores de mutação
(pequenas alterações sintaticamente
corretas) no programa original;
Verificar se os testes em T matam todos os
mutantes em M;
Se todos os mutantes foram mortos, o
conjunto T é adequado, e o processo é
encerrado.
Senão, acrescenta-se um teste para cada
mutante M sobrevivente, até que o conjunto
T seja adequado.
Análise de Mutantes
Exemplo
• Exemplos de operadores de mutação:
▪ substituição de uma variável por outra;
▪ substituição de uma variável escalar
por um vetor;
▪ substituição de um vetor por uma
variável escalar;
▪ substituição de operador relacional;
▪ substituição de operador aritmético;
▪ etc.
int max(int* input, int size){
int j, result;
result = 0;
for (j =1; j < size; j++) {
if (input[j] > input[result])
result = j;
}
return result;
teste
input[0]
}
}
1
1
input[1]
input[2]
size
Result.
Esp.
2
3
3
2
2
3
1
2
3
0
3
1
3
2
3
1
Exemplo
• Conjunto inicial:
int max(int* input, int size){
int j, result;
#
result = 0;
1
for (j =1; j < size; j++) {
if (input[j] >= input[result]) 2
result = j;
3
}
return result;
}
• Conjunto
}
input[0]
input[1]
input[2]
size
Result. esperado
Result.
obtido
1
2
3
3
2
2
3
1
2
3
0
0
1
3
2
3
1
1
que mata o mutante:
#
input[0]
input[1]
input[2]
size
Result.
esperado
Result.
obtido
1
1
2
3
3
2
2
2
3
1
2
3
0
0
3
1
3
2
3
1
1
4
1
2
2
3
1
2
4