La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

Testing basado en sintaxis: Gramáticas a partir de programas

Presentaciones similares


Presentación del tema: "Testing basado en sintaxis: Gramáticas a partir de programas"— Transcripción de la presentación:

1 Testing basado en sintaxis: Gramáticas a partir de programas
Manuel Núñez Especificación, Validación y Testing Estas transparencias están basadas en las desarrolladas por Ammann & Offutt como acompañamiento de su libro Introduction to Software Testing (2nd Edition)

2 Aplicación de testing basado en sintaxis a programas
Los criterios basados en la sintaxis nacieron con los programas y se han utilizado, esencialmente, con programas. Los criterios que usan BNF se han usado, habitualmente, para testear compiladores. Los criterios de mutation testing se han usado, habitualmente, para testing unitario y para testear la integración de clases. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

3 BNF testing para compiladores
Testear compiladores es MUY DIFÍCIL. Hay millones de programas correctos. Los compiladores deben reconocer y rechazar los programas incorrectos. Los criterios sobre BNF se pueden usar para generar programas que permitan testear las características que los compiladores deben procesar. Esta es una aplicación muy especializada y, por tanto, fuera del ámbito de este breve introducción al testing de software. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

4 Gramáticas basadas en programas
La aplicación original, y más conocida, de los métodos de testing basados en la sintaxis consiste en modificar programas. Los operadores modifican una cadena básica (programa que testeamos) para crear mutantes. Los mutantes deben compilar (deben ser cadenas válidas). Los mutantes no son tests, pero se usan para encontrar tests. Tras definir los mutantes, debemos buscar tests que provoquen el fallo de los mutantes al ejecutarlos. A este proceso se le llama matar mutantes. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

5 Especificación, Validación y Testing (M. G. Merayo y M. Núñez)
Matar mutantes Un test t mata a un mutante m de un cadena básica (programa) P sii el output observado al aplicar t a m es distinto del resultante de aplicar t a P. Si los operadores de mutación se diseñan bien entonces los tests resultantes suelen ser muy potentes. Se deben definir operadores distintos para distintos lenguajes de programación y distintos objetivos. Los testeadores añaden tests hasta que se matan todos los mutantes. Mutante muerto: Muere por un test. Mutante abortado: Sintácticamente ilegal. Mutante trivial: Lo mata casi cualquier test. Mutante equivalente: Ningún test lo puede matar (mismo comportamiento que el original). Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

6 Especificación, Validación y Testing (M. G. Merayo y M. Núñez)
Ejemplo Métodos mutados int Min (int A, int B) { int minVal; minVal = A; ∆ 1 minVal = B; if (B < A) ∆ 2 if (B > A) ∆ 3 if (B < minVal) minVal = B; ∆ Bomb (); ∆ minVal = A; ∆ minVal = failOnZero (B); } return (minVal); } // end Min Método original int Min (int A, int B) { int minVal; minVal = A; if (B < A) minVal = B; } return (minVal); } // end Min Sustituir una variable con otra Sustituir operador Fallo inmediato si se alcanza Fallo inmediato si B==0, sino no hace nada 6 mutantes Cada uno representa un programa distinto Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

7 Criterios de cobertura basado en sintaxis
Mutation Coverage (MC): Para cada m  M, RT contiene exactamente un requisito: matar a m. El modelo RIPR que vimos en la introducción de la asignatura se adapta a este marco de la siguiente forma: Alcance: El test hace que se alcance la instrucción defectuosa (en este caso, la instrucción mutada). Infección: El test hace que esta instrucción produzca un estado incorrecto. Propagación: El estado incorrecto se propaga a un output incorrecto. Revelación: El testeador debe observar parte del output incorrecto. El modelo RIPR da lugar a dos variantes de cobertura. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

8 Criterios de cobertura basado en sintaxis
Matar mutantes fuertemente Dado un mutante m de un programa P y un test t, decimos que t strongly kills m sii el output de aplicar t a P es distinto del de aplicar t a m. Matar mutantes débilmente Dado un mutante m que modifica una posición l de un programa P y un test t, decimos que t weakly kills m sii el estado de la aplicación de t a P es distinto inmediatamente después de l del de la aplicación de t a m. Matar débilmente satisface alcance e infección pero no necesariamente propagación. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

9 Especificación, Validación y Testing (M. G. Merayo y M. Núñez)
Mutación débil Weak Mutation Coverage (WMC): Para cada m  M, RT contiene exactamente un requisito: matar débilmente a m. El apelativo “débil” denota que es más fácil matar mutantes bajo este esquema. También requiere menos análisis. Usualmente, hay solo unos pocos mutantes que se pueden matar débilmente pero que no se matan fuertemente (no hay propagación). Los estudios han mostrado que los conjuntos de tests que matan débilmente a todos los mutantes matan fuertemente a casi todos los mutantes. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

10 Mutación débil: Ejemplo
El primer mutante del ejemplo Min( ) es: El test completo que mata a este mutante es: Alcance: True //Siempre se llega a la instrucción Infección: A ≠ B Propagación: (B < A) = false // Salta la instrucción Test completo: true  (A ≠ B)  ((B < A) = false) ≡ (A ≠ B)  (B ≥A) ≡ (B > A) ¿Hay algún test que mate débilmente al mutante pero no fuertemente? int Min (int A, int B) { int minVal; minVal = A; minVal = B; if (B < A) } return (minVal); } // end Min Si, por ejemplo, A=5 y B=3. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

11 Mutante equivalente: Ejemplo
El tercer mutante del ejemplo Min( ) es: La condición para que tengamos infección es: (B < A) != (B < minVal) Sin embargo, la instrucción anterior es minVal = A. Sustituyendo en el predicado anterior: (B < A) != (B < A) Al ser una contradicción, ningún input mata a este mutante. int Min (int A, int B) { int minVal; minVal = A; if (B < A) if (B< minVal) minVal = B; } return (minVal); } // end Min Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

12 Especificación, Validación y Testing (M. G. Merayo y M. Núñez)
Fuerte vs. débil 1 boolean isEven (int X) 2 { if (X < 0) X = 0 - X; ∆ X = 0; if (double) (X/2) == ((double) X) / 2.0 return (true); else return (false); 9 } Alcance: X < 0 Infección: X != 0 (X = -6) lo mata bajo mutación débil Propagación : ((double) ((0-X)/2) == ((double) 0-X) / 2.0) != ((double) (0/2) == ((double) 0) / 2.0) Esto es, X no es par … Por tanto (X = - 6) no mata fuertemente al mutante Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

13 Mutación para testear programas
Pasos automatizados Programa testeado Crear mutantes Correr heurística equivalentes Generar tests Aplicar T a P no Aplicar T a los mutantes Definir límite Eliminar tests inefectivos ¿Límite alcanzado? ¿P (T) correcto ? si no Arreglar P no

14 ¿Por qué funciona la mutación?
Premisa Fundamental de Mutation Testing Si el software contiene un defecto (fault), usualmente habrá un conjunto de mutantes que pueden ser matados solamente por un test que también detecta este defecto. ¡Esto no es una verdad absoluta! Si es verdad que, habitualmente, los mutantes guían al testeador para que construya un conjunto de tests efectivos. Un problema muy interesante: encontrar un defecto y un conjunto de tests adecuados que no encuentren dicho defecto. Por supuesto, esto depende de los operadores de mutación…. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

15 Diseño de operadores de mutación
Si nos fijamos en métodos, los operadores de mutación son similares en distintos lenguajes de programación. Los operadores de mutación hacen una de las siguientes cosas: Imitan los errores típicos de los programadores (nombres incorrectos de variables). Alientan a usar unas heurísticas de test comunes (causar que las expresiones valgan cero). Los investigadores han diseñado montones de operadores y después han seleccionado, tras experimentación, los más útiles. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

16 Diseño de operadores de mutación
Operadores de mutación efectivos Si unos tests que se han creado específicamente para matar mutantes creados por unos operadores de mutación O = {o1, o2, …} también matan mutantes creados por todos los demás operadores con una alta probabilidad, entonces O define un conjunto efectivo de operadores de mutación. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

17 Operadores de mutación en Java
ABS –– Absolute Value Insertion AOR –– Arithmetic Operator Replacement ROR –– Relational Operator Replacement COR –– Conditional Operator Replacement SOR –– Shift Operator Replacement LOR –– Logical Operator Replacement ASR –– Assignment Operator Replacement UOI –– Unary Operator Insertion UOD –– Unary Operator Deletion SVR –– Scalar Variable Replacement BSR –– Bomb Statement Replacement Pasemos a ver definiciones completas Especificación, Validación y Testing (M. G. Merayo y M. Núñez)

18 Operadores de mutación en Java
Cada expresión (y subexpresión) se sustituye por las funciones abs(), negAbs() y failOnZero(). 1. ABS –– Absolute Value Insertion: Ejemplos: a = m * (o + p); ∆1 a = abs (m * (o + p)); ∆2 a = m * abs ((o + p)); ∆3 a = failOnZero (m * (o + p)); Cada aparición de uno de los operadores aritméticos +,-,*,/, % se sustituye por los demás operadores. Además, también se sustituye por los operadores especiales leftOp y rightOp. 2. AOR –– Arithmetic Operator Replacement: Ejemplos: a = m * (o + p); ∆1 a = m + (o + p); ∆2 a = m * (o * p); ∆3 a = m leftOp (o + p);

19 Operadores de mutación en Java
Cada aparición de uno de los operadores relacionales <, ≤, >, ≥, =, ≠ se sustituye por los demás operadores y por el operador falseOP. 3. ROR –– Relational Operator Replacement: Ejemplos: if (X <= Y) ∆1 if (X > Y) ∆2 if (X < Y) ∆3 if (X falseOp Y) // devuelve false Cada aparición de uno de los operadores lógicos (and - &&, or - || , and sin evaluación condicional - &, or sin evaluación condicional - |, no equivalente - ^) se sustituye por los demás operadores. Además, también se sustituye por by falseOp, trueOp, leftOp y rightOp. 4. COR –– Conditional Operator Replacement: Ejemplos: if (X <= Y && a > 0) ∆1 if (X <= Y || a > 0) ∆2 if (X <= Y leftOp a > 0) // devuelve resultado // cláusula izquierda

20 Operadores de mutación en Java
Cada aparición de uno de los operadores <<, >>, >>> se sustituye por los demás operadores y por el operador leftOP. 5. SOR –– Shift Operator Replacement: Ejemplos: byte b = (byte) 16; b = b >> 2; ∆1 b = b << 2; ∆2 b = b leftOp 2; // resultado es b Cada aparición de uno de los operadores lógicos (bitwise and - &, bitwise or - | , or exclusivo - ^) se sustituye por los demás operadores. Además, también se sustituye por leftOp y rightOp. 6. LOR –– Logical Operator Replacement: Ejemplos: int a = 60; int b = 13; int c = a & b; ∆1 int c = a | b; ∆2 int c = a rightOp b; // resultado es b

21 Operadores de mutación en Java
Cada aparición de uno de los operadores de asignación +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, >>>= se sustituye por los demás. 7. ASR –– Assignment Operator Replacement: Ejemplos: a = m * (o + p); ∆1 a += m * (o + p); ∆2 a *= m * (o + p); Cada operador unario (+ y – aritméticos, ! condicional, ~ lógico) se inserta delante de cada expression del tipo adecuado. 8. UOI –– Unary Operator Insertion: Ejemplos: a = m * (o + p); ∆1 a = m * - (o + p); ∆2 a = -(m * (o + p));

22 Operadores de mutación en Java
Se borra cada operador unario (+ y – aritméticos, ! condicional, ~ lógico). 9. UOD –– Unary Operator Deletion: Ejemplos: if !(X <= Y && !Z) ∆1 if (X > Y && !Z) ∆2 if !(X < Y && Z) Cada variable se sustituye por todas las demás del mismo tipo y que estén declaradas en el mismo entorno. 10. SVR –– Scalar Variable Replacement: Ejemplos: a = m * (o + p); ∆1 a = o * (o + p); ∆2 a = m * (m + p); ∆3 a = m * (o + o); ∆4 p = m * (o + p);

23 Operadores de mutación en Java
Cada instrucción se sustituye por una función especial Bomb(). 11. BSR –– Bomb Statement Replacement: Ejemplos: a = m * (o + p); ∆1 Bomb() //lanza una excepción cuando se //ejecuta

24 Subsunción de otros criterios
Se considera que mutation testing es el criterio de testing más potente. ¡Y también el más caro! De largo, es el que necesita más requisitos de testing (uno por mutante). Usualmente, también es el que más tests necesita. Mutación subsume otros criterios al incluirlos implícitamente como operadores de mutación específicos. Especificación, Validación y Testing (M. G. Merayo y M. Núñez)


Descargar ppt "Testing basado en sintaxis: Gramáticas a partir de programas"

Presentaciones similares


Anuncios Google