• Nenhum resultado encontrado

Teste de Funções por Cobertura do Grafo de Fluxo de Controle

N/A
N/A
Protected

Academic year: 2021

Share "Teste de Funções por Cobertura do Grafo de Fluxo de Controle"

Copied!
6
0
0

Texto

(1)

Teste de Funções por Cobertura do Grafo de

Fluxo de Controle

Programação II

Universidade de Lisboa

Faculdade de Ciências

Departamento de Informática

Licenciatura em Tecnologias da Informação

Vasco Thudichum Vasconcelos

Fevereiro 2016

Introdução

O número de parâmetros com que uma dada função pode ser chamada é na prática ilimitado. Tal constatação torna claramente impossível a prática de tes-tes exaustivos. O método de tes-teste por partição do espaço de entrada atenta apenas na assinatura da função e no seu contrato (definido emdoctringpelas nossas

cláusulasRequireseEnsures). O método de teste por cobertura de grafos toma

um outro ponto de vista. Em vez de analisarmos os parâmetros das funções, analisamos o código da função e certificamo-nos que os testes asseguram certos critérios de cobertura do código.

Os critérios de cobertura ajudam na escolha de um número limitado de tes-tes a utilizar. Acreditamos que a utilização judiciosa de critérios de cobertura aumenta a probabilidade de encontrar falhas em programas, e que consequen-temente fornece uma garantia informal da qualidade do software.

Ao contrário dos testes por partição do espaço de entrada, a preparação de testes baseados na cobertura de grafos requer o conhecimento da linguagem de programação, Python no nosso caso.

No caso em que não dispomos do código fonte só podemos utilizar o mé-todo de partição do espaço de entrada. No entanto, na presença de código po-demos (e devemos) utilizar ambos os métodos de modo a gerar um conjunto de testes que nos permita apanhar o máximo de erros possíveis. Os dois mé-todos são complementares, explorando diferentes características do software a testar.

(2)

Construção de um grafo de controlo de fluxo

O processo de construção de testes começa com a construção do grafo de con-trolo de fluxo da função a testar. Tal grafo associa uma aresta com cada ramo no programa, e um nó com cada bloco básico. Os ramos de um programa são de-terminados pelas instruções de escolha (if), de ciclo (whileefor) e de exceção

(try). Um bloco básico é a maior sequência de instruções tal que se uma

instru-ção no bloco é executada, então todas as instruções no bloco são executadas. Uma sequência simples de instruções (um bloco básico, mais precisamente) dá lugar a um único nó no grafo. Por exemplo, ao programa abaixo corresponde um grafo com um único nó.

t = f(t, x) y = y + 1

Uma condição (umif-else) dá lugar a um grafo em losango. if x < 0: t = f(t, x) y = y + 1 else: t = f(t, -x) x<0 x>=0

Ao nó de entrada não corresponde a instrução alguma. Trata-se de um nó de decisão, representando o testex < 0e assinalando o ponto onde o programa

se divide. Temos depois dois nós correspondentes aos blocos básicos dos ra-mos “então” e “senão”. O nó na base do losango não corresponde a nenhuma instrução do programa em causa. Representa o ponto de saída do ciclo, ponto este a partir do qual se desenrola o “resto do programa”.

Representamos a carregado os nós terminais de um pedaço de código; o (único) nó de entrada é geralmente aparente nos grafos e não o representamos explicitamente.

Caso degenerado de um condicional sem ramoelse apresenta-se do

se-guinte modo. if x < 0: t = f(t, x) y = y + 1 x<0 x>=0

Este grafo só apresenta três nós; o arcox>=0leva diretamente ao nó final.

De notar que um teste em quexseja menor que0visita todos os nós no grafo,

mas não todas as arestas.

(3)

while x < n: t = f(t, x) y = y + 1

x<n x>=n

Tal como no caso da instrução conditional, o grafo começa com um nó de decisão representando desta vez o teste do ciclo. Um dos ramos leva ao nó que representa o corpo do ciclo, nó esse donde sai uma aresta que leva de volta ao nó de teste. O outro ramo do ciclo, o ramo em que o ciclo termina, leva a um nó a que não corresponde nenhuma instrução (representa o resto do programa).

E se estivermos em presença de um ciclofor? Neste caso procedemos como

se em presença de um ciclowhileestivéssemos. for y in v:

t = f(t, x)

y = y + 1 y in v fim

Do nó inicial saiem duas arestas: uma com o caso em que o ciclo prossegue, a outra para o quando o ciclo termina. O resto do grafo é em tudo análogo ao caso dowhile.

Ao contrário do que os exemplos acima podem sugerir, de um modo geral o corpo dos ciclos e os ramos das instruções condicionais não são compostos por blocos básicos. No caso de um programa com diferentes instruções (por exemplo um ciclo e um condicional), escrevemos os grafos para as várias partes e compomos o resultado. Podemos retirar do grafo final os nós falsos (que não correspondem a nenhum bloco básico ou teste) e que foram introduzidos durante o processo de construção dos condicionais ou dos ciclos.

No caso do código abaixo começámos a construção do grafo de “dentro para fora”: primeiro desenhámos o grafo do condicional e depois sobrepuse-mos o do ciclo. Finalmente juntásobrepuse-mos o bloco de entrada (y = 0et = 0). Neste

caso optámos por deixar ficar o nó falso na base do “losango” do condicional.

y = 0 t = 0 for x in v: if x < 0: t = f(t, x) y = y + 1 else: f = f(t, -x) return y y in v fim x<0 x>=0

(4)

Critérios de cobertura de grafos

Há vários tipos de critérios de cobertura de grafos. No caso da disciplina de Programação II estamos interessados apenas em critérios de cobertura de fluxo de dados. Os critérios de cobertura definem requisitos de testes em termos de propriedades de caminhos nos grafos. Um requisito de teste típico pede que o teste visite um determinado nó, que passe por uma determinada aresta ou que visite um determinado caminho (uma sequência de nós). Na disciplina de Programação II vamo-nos centrar em requisitos relacionado com caminhos.

Consideremos a seguinte função.

def sinal(x):

"""Requires: x e um numero

Ensures: devolve -1 se x negativo, 0 se x e 0, 1 caso contrário.""" if x < 0: return -1 elif x == 0: return 0 else return -1

O grafo de controlo de fluxo correspondente é o seguinte.

x<0 x>=0

x==0 x!=0

Estamos interessados em caminhos máximos, isto é, caminhos que come-çam no nó de entrada e que terminam num dos três nós terminais. Para este grafo, os requisitos de teste para o critério de cobertura de caminhos podem ser descritos deste modo:

{ visitarx<0; visitarx>=0ex==0; visitarx>=0ex!=0}

Para cobrir estes requisitos precisamos de um mínimo de três testes (porque são três os possíveis caminhos entre o nó inicial e o nó final). Eis uma possível bateria de testes.

Valor dex Resultado Cobertura

-7 -1 x<0

0 0 x>=0,x==0

327.8 1 x>=0,x!=0

Neste caso obtivémos uma cobertura de 100%, uma vez que os três requisi-tos de teste foram satisfeirequisi-tos pelos testes que concebemos.

O caso torna-se mais complicado em grafos com ciclos. Consideremos o grafo discutido anteriormente.

(5)

Se pensarmos que podemos passar no ciclo uma, duas, três, . . . , vezes, ra-pidamente percebemos que o número de testes necessários para satisfazer o critério de cobertura de todos os caminhos é infinito. Tal torna o critério im-praticável.

Para o caso de grafos com ciclos (criados por instruçõeswhileoufor)

esco-lhemos um critério mais fraco, nomeadamente o critério de cobertura de caminhos especificados. De entre todos os caminhos possíveis, temos de escolher alguns que façam sentido para o propósito de testar as nossas funções. Procuramos o equilíbrio entre um número suficientemente pequeno de testes, e a possibili-dade de descobrir falhas nas funções. Vamos então escolher caminhos que

• não passem pelo ciclo,

• passem exactamente uma vez, e • passem mais do que uma vez.

• Para cada um dos dois últimos casos, escolhemos, sempre que possível, todos os caminhos possíveis no interior dos ciclos.

Consideremos a seguinte função e o grafo de controlo de fluxo correspon-dente. def soma_absolutos(v): s = 0 for x in v: if x < 0: s = s - x else: s = s + x return s x in v fim x<0 x>=0

Os seguintes testes cobrem o critério que escolhemos. Valor dev Resultado Cobertura

[] 0 0 passagens

[-3] 3 1 passagem,x<0 [23.4] 23.4 1 passagem,x>=0 [-5,7,-2] 0 >1 passagens,x<0ex>=0

(6)

Fica como exercício escrever dois testes que passem mais do que uma vez no ciclo, um sempre pelo ramo “então”, o outro sempre pelo ramo “senão”.

Os testes garantem o que garantem

Esta metodologia de testes, como todas as outras, não garante mais do que a correção da função quando exercitada com os parâmetros dos testes. É muito fácil criar um conjunto de testes que, apesar de seguir a nossa metodologia, não identifica os erros no código. Consideremos a seguinte função.

def conta_positivos (v):

"""O número de elementos positivos em v Requires: v é uma lista de números

Ensures: Devolve o número de valores positivos em v""" conta = 0

for x in v:

if x >= 0:

conta = conta + 1

return conta

Podemos facilmente escrever um conjunto de testes que cobrem o critério de cobertura que escolhemos para Programação II. Eis um exemplo:

Valor dev Resultado Cobertura

[] 0 0 passagens [-3.14] 0 1 passagem,x<0 [23.4] 1 1 passagem,x>=0 [-5.32,7.5,-2] 1 >1 passagens,x<0ex>=0 [-3.14,-0.001] 1 >1 passagens, semprex<0 [78.4, 0.3] 2 >1 passagens, semprex>=0

É fácil de ver que estes testes não descobrem o problema no código. O erro pode no entanto ser facilmente descoberto utilizando o método de testes por partição do espaço de entrada. Neste caso bastaria pensar nas condições fronteira. Tal levaria a considerar a característica “a lista tem valores zero”, característica essa que geraria um teste que descobriria o erro.

Referências

Documentos relacionados

sistema de equações em função das condições iniciais x (0) e x (0) , as quais deverão ser obtidas das equações do sistema em análise... O mesmo não se pode dizer da

Do ponto de vista geométrico, queremos descobrir qual é o número que dista 4 unidades da origem, ou seja,.. Logo, existem 2 valores que satisfazem essa condição: -4

Obtenha a equação indicial de analise a possibilidade de obter essas soluções, à luz do Teorema de Fuchs, diante dos possíveis valores para a e c.. Caso seja possível, calcule

Sérgio Coutinho Departamento de Física – UFPE... Sérgio Coutinho Departamento de Física

Tendo sempre em vista o respeito aos direitos humanos, buscamos expor o mínimo possível a intimidade da personagem Ana Giselle, dando ênfase ao seu histórico de lutas e à forma como

Sabemos que esta potˆencia tem significado para qualquer valor real de x; no in´ıcio da contagem ´e x = 0 e antes desse instante ´e x &lt; 0.. Sabemos, tamb´em, que os valores de 1, 5

E os objetivos específicos, Discutir a importância da educação patrimonial como premissa para a preservação e conservação de bens culturais; Reconhecer como o

O primeiro objetiva desenvolver pesquisa conjunta com vistas à promoção de avanços técnico- científicos no tema “racionalização do uso da água na