Está en la página 1de 24

CRITERIOS COBERTURA DE

GRAFOS: ELEMENTOS DE DISEÑO

Estas transparencias están basadas en las desarrolladas por Ammann & Offutt como acompañamiento de
su libro Introduction to Software Testing (2nd Edition)
ORIENTACIÓN A OBJETOS Y DISEÑOS

• El énfasis en la modularidad y la reutilización lleva a que la


complejidad se desplace a las relaciones/conexiones entre las partes
del sistema.
• Por ello, testear estas relaciones es más importante que antes.
• Los grafos se basan en las conexiones entres las componentes
software.
GRAFO DE LLAMADAS

A
• Es el tipo de grafo más común para testear el diseño
estructural.
B C D
• Nodos: Unidades (en Java, métodos).
• Aristas: Llamadas a unidades.
E F
• Node coverage: Llamar a cada unidad al menos una vez
(cobertura de métodos).
Ejemplo de grafo
• Edge coverage: Ejecutar cada llamada al menos una vez
de llamadas
(cobertura de llamadas).
GRAFO DE LLAMADAS SOBRE CLASES

• La cobertura de nodos y aristas sobre el grafo de llamadas de


clases, habitualmente, no funciona bien.
• Por ejemplo, los métodos podrían no llamarse entre ellos.

Clase stack ???


public void push (Object o)
public Object pop ( )
public boolean isEmpty (Object o)
push pop isEmpty

Necesitamos otros tipos de testing: NO usen


criterios basados en grafos.
HERENCIA Y POLIMORFISMO

• Trabajos todavía en fases muy preliminares.

Las clases no son A


A objetos
ejecutables (no
a
podemos testear este
grafo). B
B
b
Necesitamos objetos.
C D
C D
c d
Ejemplo grafo
jerarquía de ¿Cuál es la cobertura en
herencias este grafo?
COBERTURA EN UN GRAFO DE
HERENCIAS

• ¿Crear un objeto para cada clase?


• Esto es demasiado débil porque no hay ejecución.
• ¿Crear un objeto para cada clase y aplicar cobertura de llamadas?
• OO Call Coverage: RT contiene cada nodo alcanzable en el grafo de
llamadas de un objeto que se ha instanciado para cada clase de la
jerarquía.
• OO Object Call Coverage: RT contiene cada nodo alcanzable en el
grafo de llamadas de todos los objetos que se han instanciado para
cada clase de la jerarquía.
SOFTWARE Y DISEÑO OO

• El énfasis en la modularidad y la reutilización pone complejidad en las


conexiones de diseño.
• Probar las relaciones de diseño es más importante que antes.
• Los gráficos se basan en las conexiones entre los componentes de software.
• Las conexiones son relaciones de dependencia, también llamadas acoplamiento.
FLUJO DE DATOS A NIVEL DE DISEÑO

• Los acoplamientos de flujo de datos entre unidades y clases son más


complicados que los acoplamientos de flujo de control
• Cuando se pasan los valores, "cambian de nombre“
• Muchas formas diferentes de compartir datos
• Encontrar defs y uses puede ser difícil: encontrar qué usos puede alcanzar un def es
muy difícil
• Cuando el software se complica ... Los testers deberían estar muy interesados
porque ¡Ahí es donde están las fallas!
DEFINICIONES PRELIMINARES

• Llamador: una unidad que invoca otra unidad

• Destinatario de la llamada: La unidad a la que se llama

• Llamado del sitio: Instrucción o nodo donde aparece la llamada

• Parámetro real: Variable en el llamador

• Parámetro formal: Variable en el destinatario de la llamada


EJEMPLO DE LLAMADO DE SITIO

• La aplicación de criterios de flujo de datos a pares def-use entre unidades es


demasiado costosa
• Demasiadas posibilidades
• Pero esto es prueba de integración, y realmente solo nos importa la interfaz ...

A Llamador

B (x)
 Parámetro real
end A
interfaz
B (Y) Llamador

end B Parámetro
formal
INTER- PROCEDIMIENTO DE A PARES

• Si nos centramos en la interfaz, entonces solo tenemos que considerar las


últimas definiciones de variables antes de las llamadas y devoluciones, y los
primeros usos dentro de las unidades y después de las llamadas.
• Last-def : El conjunto de nodos que definen una variable x y tiene una ruta def-
clear desde el nodo a través de un sitio de llamado hasta un uso en la otra
unidad.
• Puede ser de persona que llama a destinatario (parámetro o variable compartida) o
de persona a persona que llama como valor devuelto.
• Primer uso: El conjunto de nodos que tienen usos de una variable y y para los
cuales hay una ruta def-clear y use-clear desde el sitio de llamada a los nodos.
EJEMPLO DE INTER- PROCEDIMIENTO DE A
PARES
Caller
F X = 14 last-def

y = G (x) Llamado del sitio
DU pair 
print (y)
first-use

Callee
G (a) print (a)

first-use
DU pair
b = 42

last-def
return (b)
EJEMPLO DE INTER- PROCEDIMIENTO DE A
PARES
10 B (int y)
1 x=5

11 Z = y 12 T=y
2 x=4

13 print (y)
3 x=3

Last Defs
4 B (x) 2, 3
First Uses
11, 12
DU Pairs
(A, x, 2)—(B, y, 11)
(A, x, 2)—(B, y, 12)
(A, x, 3)—(B, y, 11)
(A, x, 3)—(B, y, 12)
EJEMPLO - CUADRÁTICA

1 // Programa para calcular la raíz cuadrática para dos 25 ok = Root (X,Y, Z);
números 26 if (ok)
2 import java.lang.Math; 27 System.out.println
3 28 (“Quadratic: ” + Root1 + Root2);
4 class Quadratic 29 else
5{ 30 System.out.println (“No Solution.”);
6 private static float Root1, Root2; 31 }
7 32
8 public static void main (String[] argv) 33 // Tres enteros positivos, encuentra raíz cuadrática
9 { 34 private static boolean Root (int A, int B, int C)
10 int X,Y, Z; 35 {
11 boolean ok; 36 double D;
12 int controlFlag = Integer.parseInt (argv[0]); 37 boolean Result;
13 if (controlFlag == 1) 38 D = (double) (B*B) - (double) (4.0*A*C );
14 { 39 if (D < 0.0)
15 X = Integer.parseInt (argv[1]); 40 {
16 Y = Integer.parseInt (argv[2]); 41 Result = false;
17 Z = Integer.parseInt (argv[3]);
18 } 42 return (Result);
19 else 43 }
20 { 44 Root1 = (double) ((-B + Math.sqrt(D))/(2.0*A));
45 Root2 = (double) ((-B – Math.sqrt(D))/(2.0*A));
21 X = 10; 46 Result = true;
22 Y = 9; 47 return (Result);
23 Z = 12; 48 } // End method Root
24 } 49 } // End class Quadratic
1 // Programa para calcular la raíz cuadrática para dos números
2 import java.lang.Math;
3
4 class Quadratic
5{
6 private static float Root1, Root2; Variables
7 compartidas
8 public static void main (String[] argv)
9 {
10 int X, Y, Z;
11 boolean ok;
12 int controlFlag = Integer.parseInt (argv [0]);
13 if (controlFlag == 1)
14 {
15 X = Integer.parseInt (argv [1]);
last-defs 16 Y = Integer.parseInt (argv [2]);
17 Z = Integer.parseInt (argv [3]);
18 }
19 else
20 {
21 X = 10;
22 Y = 9;
23 Z = 12;
24 }
25 ok = Root (X, Y, Z);
first-use 26 if (ok)
27 System.out.println
28 (“Quadratic: ” + Root1 + Root2);
29 else
30 System.out.println (“No Solution.”);
31 }
32
33 // Tres enteros positivos, encuentra raíz cuadrática
34 private static boolean Root (int A, int B, int C)
first-use 35 {
36 double D;
37 boolean Result;
38 D = (double) (B*B) - (double) (4.0*A*C);
39 if (D < 0.0)
40 {
last-def 41 Result = false;
42 return (Result);
43 }
44 Root1 = (double) ((-B + Math.sqrt (D)) / (2.0*A));
last-defs 45 Root2 = (double) ((-B – Math.sqrt (D)) / (2.0*A));
46 Result = true;
47 return (Result);
48 } / /End method Root
49 } // End class Quadratic
CUADRATICA – ACOPLAMIENTO DE A PARES

• Pares de ubicaciones: nombre del método, nombre de la variable, instrucción


• (main (), X, 15) – (Root (), A, 38)
• (main (),Y, 16) – (Root (), B, 38)
• (main (), Z, 17) – (Root (), C, 38)
• (main (), X, 21) – (Root (), A, 38)
• (main (),Y, 22) – (Root (), B, 38)
• (main (), Z, 23) – (Root (), C, 38)
• (Root (), Root1, 44) – (main (), Root1, 28)
• (Root (), Root2, 45) – (main (), Root2, 28)
• (Root (), Result, 41) – ( main (), ok, 26 )
• (Root (), Result, 46) – ( main (), ok, 26 )
NOTAS DE FLUJO DE DATOS DE
ACOPLAMIENTO

• Sólo las variables que se utilizan o definen en el destinatario de la llamada


• Inicializaciones implícitas de variables globales y de clase
• Los acoplamiento de a pares transitivos son demasiado caros de manejar
• A llama a B, B llama a C, y hay una variable definida en A y usada en C
• Matrices: una referencia a un elemento se considera una referencia a todos los
elementos
HERENCIA, POLIMORFISMO Y BINDING
DINÁMICO

• El control adicional y las conexiones de datos hacen que el análisis del flujo de
datos sea más complejo
• Las unidades de definición y uso pueden estar en diferentes jerarquías de
llamada
• Cuando se utilizan jerarquías de herencia, una definición en una unidad podría
alcanzar usos en cualquier clase de la jerarquía de herencia
• Con el enlace dinámico, la misma ubicación puede alcanzar diferentes usos
dependiendo del tipo actual del objeto usando.
• ¡La misma ubicación puede tener diferentes definiciones o usos en diferentes
puntos de la ejecución!
DEFINICIONES ADICIONALES

• Herencia: Si la clase B hereda de la clase A, entonces todas las variables y métodos


en A están implícitamente en B, y B puede agregar más
• A es el padre o antepasado
• B es el hijo o descendiente
• A un objeto de referencia obj que se declara de tipo A se le puede asignar un
objeto de tipo A, B o cualquiera de los descendientes de B
• Tipo declarado : Tipo utilizado en la declaración: A obj;
• Tipo real: Tipo utilizado en la asignación de objetos: obj = new B();
• Variables de clase (estado): Las variables declaradas en el nivel de clase, a menudo
privadas
TIPO DEF USANDO PARES

def
def last-def
A () A ()
use
Flujo de datos
B () first-use interprocedimentales
A () use B ()
Flujo de datos intra-
procedimiento total acoplada
(dentro de la misma unidad)

def
A() M () A() def
A()
M() A()

B() use N()


B() use
B()
N() B()
F () F()

Flujo de datos de acoplamiento Flujo de datos de acoplamiento indirecto


directo orientado a objetos orientado a objetos
RESUMEN DEL FLUJO DE DATOS OO

• Los defs y uses pueden estar en la misma clase, o en diferentes


clases
• Los investigadores han aplicado las pruebas de flujo de datos a la
situación OO de acoplamiento directo
• No se ha utilizado en la práctica
• No hay herramientas disponibles
• Las pruebas de flujo de datos de acoplamiento indirecto no se
han probado ni en la investigación ni en la práctica
• El costo del análisis puede ser prohibitivo
APLICACIONES WEB Y OTRO SOFTWARE
DISTRIBUIDO

• “message” podría ser HTTP, RMI, u otro mecanismo


• A() y B() podrían estar en la misma clase o acceder a un
Variable persistente, como en una sesión web
• Más allá de las tecnologías actuales
P1 message P2

def use
A() B()

Flujo de datos de software distribuido


EN RESUMEN: ¿QUÉ FUNCIONA?

• Los gráficos de llamadas son formas comunes y muy útiles de diseñar pruebas
de integración
• El flujo de datos entre procedimientos es relativamente fácil de calcular y da
como resultado pruebas de integración efectivas
• Las ideas para el software OO y las aplicaciones web son preliminares y no se
han utilizado mucho en la práctica.

También podría gustarte