En ingeniería de software , la cobertura de código es una medida porcentual del grado en que se ejecuta el código fuente de un programa cuando se ejecuta un conjunto de pruebas en particular. Un programa con una cobertura de prueba alta ejecuta una mayor parte de su código fuente durante la prueba, lo que sugiere que tiene menos posibilidades de contener errores de software no detectados en comparación con un programa con una cobertura de prueba baja. [1] [2] Se pueden utilizar muchas métricas diferentes para calcular la cobertura de la prueba. Algunos de los más básicos son el porcentaje de subrutinas del programa y el porcentaje de declaraciones del programa llamadas durante la ejecución del conjunto de pruebas.
La cobertura de pruebas fue uno de los primeros métodos inventados para las pruebas sistemáticas de software . La primera referencia publicada fue por Miller y Maloney en Communications of the ACM , en 1963. [3]
Para medir qué porcentaje de código ha sido ejecutado por un conjunto de pruebas , se utilizan uno o más criterios de cobertura . Por lo general, se definen como reglas o requisitos que un conjunto de pruebas debe satisfacer. [4]
Existen varios criterios de cobertura, pero los principales son: [5]
Por ejemplo, considere la siguiente función C :
int foo ( int x , int y ) { int z = 0 ; si (( x > 0 ) && ( y > 0 )) { z = x ; } devolver z ; }
Supongamos que esta función es parte de un programa más grande y que este programa se ejecutó con algún conjunto de pruebas.
foo
fue llamada al menos una vez.foo(1,1)
, porque en este caso, se ejecutarían todas las líneas de la función, incluida z = x;
.foo(1,1)
y foo(0,1)
porque, en el primer caso, if
se cumplen ambas condiciones y z = x;
se ejecuta, mientras que en el segundo caso, la primera condición, (x>0)
, no se cumple, lo que impide la ejecución de z = x;
.foo(1,0)
, foo(0,1)
y foo(1,1)
. Estos son necesarios porque en el primer caso (x>0)
se evalúa como true
, mientras que en el segundo se evalúa como false
. Al mismo tiempo, el primer caso genera (y>0)
false
, el segundo caso no evalúa (y>0)
(debido a la evaluación diferida del operador booleano), el tercer caso lo hace true
.En lenguajes de programación que no realizan evaluación de cortocircuito , la cobertura de condición no implica necesariamente cobertura de rama. Por ejemplo, considere el siguiente fragmento de código Pascal :
si a y b entonces
La cobertura de la condición se puede satisfacer mediante dos pruebas:
a=true
,b=false
a=false
,b=true
Sin embargo, este conjunto de pruebas no satisface la cobertura de sucursales ya que ninguno de los casos cumplirá la if
condición.
Puede ser necesaria la inyección de errores para garantizar que todas las condiciones y ramas del código de manejo de excepciones tengan una cobertura adecuada durante las pruebas.
Una combinación de cobertura de funciones y cobertura de sucursales a veces también se denomina cobertura de decisiones . Este criterio requiere que cada punto de entrada y salida del programa haya sido invocado al menos una vez, y que cada decisión del programa haya asumido todos los resultados posibles al menos una vez. En este contexto, la decisión es una expresión booleana que comprende condiciones y cero o más operadores booleanos. Esta definición no es la misma que la de cobertura de sucursales, [6] sin embargo, el término cobertura de decisiones a veces se utiliza como sinónimo. [7]
La cobertura de condición/decisión requiere que se cumplan tanto la cobertura de decisión como la de condición. Sin embargo, para aplicaciones críticas para la seguridad (como el software de aviónica ), a menudo se requiere que se cumpla la condición/cobertura de decisión modificada (MC/DC) . Este criterio amplía los criterios de condición/decisión con requisitos de que cada condición debe afectar el resultado de la decisión de forma independiente.
Por ejemplo, considere el siguiente código:
si ( a o b ) yc entonces _
Los criterios de condición/decisión se cumplirán mediante el siguiente conjunto de pruebas:
Sin embargo, el conjunto de pruebas anterior no satisfará la cobertura de condición/decisión modificada, ya que en la primera prueba, el valor de 'b' y en la segunda prueba el valor de 'c' no influirían en el resultado. Por lo tanto, se necesita el siguiente conjunto de prueba para satisfacer MC/DC:
Este criterio requiere que se prueben todas las combinaciones de condiciones dentro de cada decisión. Por ejemplo, el fragmento de código de la sección anterior requerirá ocho pruebas:
La cobertura de valores de parámetros (PVC) requiere que en un método que toma parámetros, se consideren todos los valores comunes para dichos parámetros. La idea es que se prueben todos los valores posibles comunes para un parámetro. [8] Por ejemplo, los valores comunes para una cadena son: 1) nulo , 2) vacío, 3) espacios en blanco (espacio, tabulaciones, nueva línea), 4) cadena válida, 5) cadena no válida, 6) cadena de un solo byte, 7 ) cadena de doble byte. También puede ser apropiado utilizar hilos muy largos. No probar cada valor de parámetro posible puede provocar un error. Probar solo una de estas podría dar como resultado una cobertura de código del 100 % ya que cada línea está cubierta, pero como solo se prueba una de siete opciones, solo hay un 14,2 % de PVC.
Existen otros criterios de cobertura, que se utilizan con menos frecuencia:
A menudo se requiere que las aplicaciones confiables o críticas para la seguridad demuestren el 100 % de algún tipo de cobertura de prueba. Por ejemplo, el estándar ECSS -E-ST-40C exige una cobertura de declaraciones y decisiones del 100% para dos de cuatro niveles de criticidad diferentes; para los demás, los valores de cobertura objetivo dependen de la negociación entre proveedor y cliente. [11] Sin embargo, el establecimiento de valores objetivo específicos - y, en particular, el 100% - ha sido criticado por los profesionales por varias razones (cf. [12] ). Martin Fowler escribe: "Yo sospecharía de algo como el 100% - sería Huele a alguien escribiendo pruebas para alegrar los números de cobertura, pero sin pensar en lo que está haciendo". [13]
Algunos de los criterios de cobertura anteriores están relacionados. Por ejemplo, la cobertura del camino implica cobertura de decisión, declaración y entrada/salida. La cobertura de decisiones implica cobertura de declaraciones, porque cada declaración es parte de una rama.
La cobertura completa del camino, del tipo descrito anteriormente, suele ser poco práctica o imposible. Cualquier módulo con una sucesión de decisiones puede tener hasta rutas dentro de él; Las construcciones de bucles pueden dar como resultado un número infinito de caminos. Muchas rutas también pueden ser inviables, ya que no hay ninguna entrada al programa bajo prueba que pueda causar que se ejecute esa ruta en particular. Sin embargo, se ha demostrado que un algoritmo de propósito general para identificar rutas inviables es imposible (dicho algoritmo podría usarse para resolver el problema de la detención ). [14] La prueba de ruta básica es, por ejemplo, un método para lograr una cobertura completa de sucursales sin lograr una cobertura completa de ruta. [15]
En cambio, los métodos para las pruebas prácticas de cobertura de rutas intentan identificar clases de rutas de código que difieren sólo en el número de ejecuciones de bucle y, para lograr una cobertura de "ruta básica", el evaluador debe cubrir todas las clases de ruta. [ cita necesaria ] [ aclaración necesaria ]
El software de destino se construye con opciones o bibliotecas especiales y se ejecuta en un entorno controlado, para asignar cada función ejecutada a los puntos de función en el código fuente. Esto permite probar partes del software de destino a las que rara vez o nunca se accede en condiciones normales, y ayuda a garantizar que se hayan probado las condiciones más importantes (puntos de función). Luego, el resultado resultante se analiza para ver qué áreas de código no se han ejercitado y las pruebas se actualizan para incluir estas áreas según sea necesario. Combinado con otros métodos de cobertura de pruebas, el objetivo es desarrollar un conjunto de pruebas de regresión rigurosas pero manejables.
Al implementar políticas de cobertura de pruebas dentro de un entorno de desarrollo de software, se debe considerar lo siguiente:
Los autores de software pueden observar los resultados de la cobertura de las pruebas para diseñar pruebas adicionales y conjuntos de entradas o configuraciones para aumentar la cobertura de funciones vitales. Dos formas comunes de cobertura de prueba son la cobertura de declaración (o línea) y la cobertura de rama (o borde). La cobertura de línea informa sobre la huella de ejecución de las pruebas en términos de qué líneas de código se ejecutaron para completar la prueba. La cobertura de borde informa qué ramas o puntos de decisión de código se ejecutaron para completar la prueba. Ambos informan una métrica de cobertura, medida como porcentaje. El significado de esto depende de qué forma(s) de cobertura se hayan utilizado, ya que una cobertura de sucursal del 67% es más completa que una cobertura de estado de cuenta del 67%.
Generalmente, las herramientas de cobertura de prueba requieren cálculo y registro además del programa real, lo que ralentiza la aplicación, por lo que normalmente este análisis no se realiza en producción. Como era de esperar, hay clases de software que no pueden someterse de manera factible a estas pruebas de cobertura, aunque se puede aproximar cierto grado de mapeo de cobertura mediante análisis en lugar de pruebas directas.
También hay algunos tipos de defectos que se ven afectados por este tipo de herramientas. En particular, algunas condiciones de carrera u operaciones similares sensibles en tiempo real pueden enmascararse cuando se ejecutan en entornos de prueba; aunque, por el contrario, algunos de estos defectos pueden resultar más fáciles de encontrar como resultado de la sobrecarga adicional del código de prueba.
La mayoría de los desarrolladores de software profesionales utilizan la cobertura C1 y C2. C1 significa cobertura de estado de cuenta y C2 para cobertura de sucursal o condición. Con una combinación de C1 y C2, es posible cubrir la mayoría de las declaraciones en una base de código. La cobertura de declaraciones también cubriría la cobertura de funciones con entrada y salida, bucle, ruta, flujo de estado, flujo de control y cobertura de flujo de datos. Con estos métodos, es posible lograr casi el 100% de cobertura de código en la mayoría de los proyectos de software.[17]
La cobertura de las pruebas es una consideración en la certificación de seguridad de los equipos de aviónica. Las pautas según las cuales la Administración Federal de Aviación (FAA) certifica los equipos de aviónica están documentadas en DO-178B [16] y DO-178C . [18]
La cobertura de la prueba también es un requisito en la parte 6 de la norma de seguridad automotriz ISO 26262 Vehículos de carretera - Seguridad funcional . [19]