Seminario web destacado: Presentación de la prueba CT de Parasoft C/C++ para pruebas continuas y excelencia en el cumplimiento | Vea ahora

Cobertura del código de medición: guía para pruebas efectivas

Foto de cabeza de Ricardo Camacho, Director de Cumplimiento de Seguridad y Seguridad
20 de noviembre.
10 min leer

La cobertura del código depende en gran medida de la precisión. Implica elegir solo la cobertura adecuada requerida para su proyecto. En este artículo se analizan detalladamente los dos problemas principales de la cobertura de código, junto con la forma de prevenirlos.

¿Qué es la cobertura del código?

La esencia de la cobertura de código es exponer el código que no se ha ejecutado tras haber realizado las pruebas de software. El código descubierto deja claro dónde pueden haber defectos ocultos en la aplicación y que faltan casos de prueba para abordar estas áreas descubiertas.

El enfoque más común para monitorear el código a medida que se ejecuta y lograr cobertura del código es instrumentar el código. Esto significa que el código existente está adornado con código adicional y se puede adaptar aún más para detectar si se han ejecutado estructuras de codificación como una declaración, función, condición, decisión, rama y otras. Esto es importante porque habrá varias rutas lógicas de ejecución que se pueden tomar, por lo que debes asegurarte de haberlas ejercitado y haber expuesto comportamientos inseguros, inseguros o impredecibles.

Beneficios de medir la cobertura del código

No desea cobertura por la cobertura misma. Necesita una cobertura significativa que indique que ha hecho un buen trabajo probando el software. Además de encontrar lagunas en las pruebas, la cobertura del código también expone código inactivo.

El código muerto es código que existe en la aplicación, pero no existe ningún escenario de ejecución posible para que ese código se ejecute alguna vez. A veces nuestros requisitos cambian, por lo que alteramos la lógica. En sistemas de software complejos, no nos damos cuenta de todos los resultados y efectos, como por ejemplo tener una función o estructura de código que nunca se ejecutará. Por lo tanto, el código inactivo puede indicar un defecto en la lógica, pero, como mínimo, es un riesgo de seguridad que debe resolverse.

También existen beneficios para el usuario final o las partes interesadas al medir la cobertura del código. Si la aplicación ha sido probada y se ha cubierto el 100% del código, proporciona esa sensación cálida y confusa de entregar algo de calidad, en comparación con la de entregar una aplicación con solo el 60% de cobertura de código alcanzada. Esta es también la razón por la que los estándares de seguridad funcional de la industria como DO-178C, ISO 26262, IEC 62304, IEC 61508, EN 50128 y muchos otros exigen o recomiendan encarecidamente que los equipos de desarrollo realicen la cobertura del código.

Métricas de cobertura de código

Los estándares funcionales también guían el tipo de métricas de cobertura que se deben alcanzar y algunos de los diversos métodos de prueba que se deben utilizar. Como se mencionó anteriormente, existen estructuras de codificación como declaraciones, ramas, decisiones, etc., que solo se ejercen a través de condiciones muy específicas. Estas condiciones dependen de variables específicas y de sus valores en el momento adecuado.

En función de las condiciones adecuadas, se pueden realizar un seguimiento de las distintas rutas de ejecución y recopilar métricas. Por lo tanto, es necesario crear varios casos de prueba para alimentar la aplicación con los datos correctos y crear la condición deseada.

Para recopilar métricas de cobertura de código, los equipos pueden utilizar varios métodos de cobertura de prueba, como pruebas unitarias, pruebas de sistemas, pruebas manuales y más.

Cobertura de prueba

Dado que los equipos de control de calidad (QA) tienen que realizar pruebas del sistema, muchas organizaciones utilizan los casos de prueba del sistema para obtener cobertura del código. Sin embargo, es común que las pruebas a nivel de sistema no proporcionen los objetivos de cobertura necesarios. Por lo general, produce una cobertura del 60%, lo que deja mucho espacio para problemas no descubiertos. Por lo tanto, los equipos pueden terminar agregando cobertura de pruebas unitarias, pruebas de integración y pruebas manuales.

Juntos, estos métodos de prueba pueden llevarlo a una cobertura de código del 100% o al objetivo deseado. Pero las organizaciones también deben comprender el nivel de cobertura del código estructural que se requiere. Los estándares de seguridad funcional exigen o recomiendan que la cobertura del código consista en una cobertura de declaración, rama y/o decisión de condición modificada (MC/DC). Esto está determinado por el nivel de integridad de seguridad (SIL) establecido en su aplicación.

Cuanto mayor sea el riesgo para las personas y la propiedad si se produce una falla del software, más conjuntos de cobertura de códigos estructurales se requerirán. Los requisitos de cobertura de código más estrictos existen en las aplicaciones del estándar de aviónica DO-178C Nivel A, donde la cobertura de código a nivel de código ensamblador es además de declaración, rama y MC/DC. Afortunadamente para nuestros clientes, Parasoft automatiza la cobertura del código ensamblador, también conocida como verificación de código objeto, como parte de nuestra oferta de soluciones.

Cobertura de estados de cuenta

La cobertura de declaraciones responde si se ha ejecutado cada declaración en la aplicación de software. Un enunciado es una unidad sintáctica única del lenguaje de programación que expresa alguna acción a realizar.

Aquí hay un ejemplo de declaración simple: int* ptr = ptr + 5;

Cobertura de condición

La cobertura de condiciones responde a la pregunta: ¿Se ha evaluado cada subexpresión booleana como verdadera y falsa? Las condiciones se evalúan como verdaderas o falsas según operadores de relación como ==, !=, <, > y otros. Se realizan diferentes caminos de ejecución en función del resultado evaluado. Entonces, para la cobertura de una condición como (A > 7), necesitará dos casos de prueba. Un caso de prueba donde A es igual a 0, lo que satisface un resultado verdadero, y un caso de prueba donde A es igual a 9, lo que satisface un resultado falso.

Cobertura de decisiones

La cobertura de decisiones responde a la pregunta: ¿Se ha evaluado cada subexpresión no booleana como verdadera y falsa? Las decisiones son expresiones compuestas de condiciones y uno o más de los operadores lógicos && o ||. Para lograr cobertura de decisión para una decisión como ((A>7) && (B<=0)), necesita casos de prueba que demuestren un resultado verdadero y falso para cada decisión.

  • Un caso de prueba donde A es mayor que 7 y B es menor o igual a 0, satisface un resultado verdadero.
  • Un caso de prueba en el que A es menor que 7 o B es mayor que 0 satisface un resultado falso.

Es importante señalar que el plazo de cobertura de la decisión se ha sobrecargado. Para algunas industrias, la cobertura de decisiones significa cobertura de sucursales.

Cobertura de sucursales

La cobertura de sucursales responde a la pregunta: ¿Se ha ejecutado cada “ruta” en una estructura de control de condiciones y decisiones (si, cambio, captura mientras, etc.)? En algunas construcciones de código complicadas, la cobertura de sucursales es insuficiente, por lo que se recomienda la cobertura de decisión de condición modificada además de la cobertura de sucursales.

Cobertura de decisión de condición modificada (MC/DC)

MC/DC responde a la pregunta: ¿Se han evaluado todas las condiciones dentro de las decisiones para todos los resultados posibles al menos una vez? Es una combinación de cobertura de sucursal, condición y decisión, pero mucho más sólida. Asegura que:

  • Cada declaración de control toma todos los resultados posibles al menos una vez.
  • Cada condición toma todos los resultados posibles al menos una vez.
  • Se ha demostrado que cada condición en una decisión afecta de forma independiente el resultado de esa decisión.

Tabla que muestra los casos de prueba MC/DC con cobertura del 100 % con condiciones y resultados.

Si toma la declaración de decisión de condición (C1 || (C2 && C3)) que se muestra en la tabla, tiene:

  • Tres condiciones: C1, C2 y C3
  • Dos decisiones: el OR (||) y el AND (&&)

Según los requisitos de MC/DC, si la condición 1 es falsa y debido a que la decisión es una OR, es necesario evaluar las condiciones 2 y 3 para determinar el efecto en el resultado.

Sin embargo, si la condición 1 es verdadera, entonces el resultado es automáticamente verdadero y no es necesario evaluar las condiciones 2 y 3.

Existe una fórmula para MC/DC para determinar el número mínimo de casos de prueba necesarios para satisfacer una cobertura de MC/DC del 100 %. Es el número de condiciones más 1. La tabla ilustra esto claramente.

Otros tipos de cobertura

Más tipos de cobertura responden a las siguientes preguntas.

  • Cobertura de funciones. ¿Se ha ejecutado cada declaración de una función en la aplicación?
  • Cobertura de llamadas. ¿Se ha llamado a cada función del programa?
  • Cobertura de línea. ¿Se han ejecutado todas las líneas del programa?
  • Cobertura de bordes. ¿Se han ejecutado todas las ramas del programa?
  • Cobertura de camino. ¿Se han ejecutado todas las rutas posibles a través de una parte determinada del código?
  • Cobertura de entrada/salida. ¿Se han ejecutado todas las llamadas y retornos posibles de la función?
  • Cobertura de bucle. ¿Se han ejecutado todos los bucles posibles cero veces, una vez y más de una vez?
  • Cobertura de bloque. ¿Se ha ejecutado cada grupo de declaraciones en el bucle principio-fin o if-else, case, wait, while o for, etc.?

¿Cómo medir la cobertura del código?

Dado que existen varios tipos de estructuras de cobertura de código, existen métricas de cobertura de código para cada uno. Si su objetivo o requisito es una cobertura del 100 % del extracto, de la sucursal y de MC/DC, debe cumplir con una cobertura del 100 % del extracto, del 100 % de la sucursal y del 100 % de MC/DC.

También hay construcciones de codificación en las que no se pueden crear casos de prueba para alcanzar una línea de código en particular. Por ejemplo, una declaración de retorno sigue un bucle infinito. Para aplicaciones críticas para la seguridad donde se exige una cobertura del 100 % de las declaraciones, los usuarios pueden medir y contabilizar esa línea de código recorriéndola paso a paso en un depurador. Esta inspección visual es aceptable y válida como método para medir la cobertura del código. Para facilitar el esfuerzo necesario para recopilar la cobertura del código, es importante seleccionar la mejor solución disponible. Parasoft es esa solución.

Captura de pantalla de la prueba de Parasoft C/C++ con cobertura de declaración seleccionada como tipo, preparándose para ejecutar pruebas unitarias.

Paso 1. Elija una herramienta de cobertura de código

Como se mencionó, la cobertura del código se recopila mediante el uso de varios métodos de prueba, como pruebas manuales, pruebas unitarias, pruebas del sistema y más. Además, el código está instrumentado para detectar la ejecución de código y recopilar varios tipos de cobertura de código estructural como declaración, rama y MC/DC.

Además, para sistemas críticos para la seguridad, algunas partes interesadas exigen realizar una cobertura de código en el hardware de destino real y certificar la herramienta de cobertura para su uso en sistemas críticos para la seguridad. Por lo tanto, elegir un herramienta de cobertura de código Es un paso extremadamente importante porque allana el camino para un viaje fluido y productivo.

Paso 2. Integra la herramienta

Las soluciones de cobertura de código de Parasoft se integran perfectamente en IDE como Eclipse, MSVS, VS Code y muchos otros editores, lo que hace que su uso e implementación sean intuitivos. Las organizaciones también pueden optar por instrumentar su aplicación y ejecutar sus pruebas existentes y, además, se genera un informe de cobertura.

La mayoría de los clientes integran la solución de cobertura de código de Parasoft en su canal de integración continua (CI). Como parte del proceso de construcción, se instrumenta el código. Durante la fase de prueba de DevOps, se captura la cobertura del código. Para aplicaciones críticas para la seguridad, la cobertura del código también se puede capturar durante la ejecución del código en el hardware de destino.

Paso 3. Escribir y ejecutar pruebas

Herramientas de prueba y desarrollo de Parasoft como Prueba C / C ++ y jprueba automatice la creación de casos de prueba para pruebas unitarias, pruebas de integración y pruebas de sistemas. Funciones como la creación automatizada de casos de prueba unitaria pueden generar hasta un 80 % de cobertura de código o más a partir de la ejecución de los casos de prueba que se crean automáticamente.

Los casos de prueba generados automáticamente también son casos de prueba inteligentes, lo que significa que se analiza el código y se crean casos de prueba para exponer defectos reales, como condiciones fuera de límites, punteros nulos, desbordamientos de búfer, división por cero y más. Esta solución con solo presionar un botón permite enormes ahorros en mano de obra y un increíble aumento de la productividad. Los editores de GUI y la función de asistente con guía paso a paso facilitan la creación de casos de prueba.

Paso 4. Generar informe de cobertura

Los informes de cobertura de código de Parasoft son excepcionales. Con líneas resaltadas y codificadas por colores visibles dentro de su IDE o editor de código favorito, DTP genera archivos de código en color para detectar visualmente líneas de código que no han sido probadas y no están disponibles para fines de auditoría. Lo más convincente es Solución de informes y análisis de panel web DTP de Parasoft. Muestra gráficos de cobertura de código en progreso, áreas de riesgo y widgets enfocados en la cobertura de estados de cuenta, cobertura de sucursales y más. Estos son exactamente los datos completos que la administración necesita para monitorear el progreso más allá de la cobertura del código. También muestra el estado del proyecto y la calidad del código en todos sus aspectos.

Paso 5. Revisar y mejorar

Debido a que nuestras soluciones de prueba de software y cobertura de código están diseñadas específicamente para integrarse en su flujo de trabajo de CI/CD, los equipos pueden revisar su progreso en cada reunión de revisión de sprint, adaptarse a los cambios en los requisitos y mejorar los procesos que mejoran la productividad y la calidad del código. Las soluciones de Parasoft están diseñadas para admitir metodologías ágiles modernas. Es por eso que Parasoft se integra con GitHub, GitLab, Azure DevOps, Bazel, Jira, Jenkins, Bamboo y más.

Las dos grandes trampas de la cobertura del código

La medición de la cobertura del código es una de esas cosas que siempre me llama la atención. Por un lado, a menudo encuentro que las organizaciones no necesariamente saben cuánto código están cubriendo durante las pruebas, ¡lo cual es sorprendente! En el otro extremo del espectro de cobertura, hay organizaciones para las que el número es tan importante que la calidad y eficacia de las pruebas se han vuelto prácticamente irrelevantes.

La cobertura del código puede ser un número bueno e interesante para evaluar la calidad de su software, pero es importante recordar que es un medio, más que un fin. No queremos cobertura por cubrir. Queremos cobertura porque se supone que indica que hemos hecho un buen trabajo probando el software. Si las pruebas en sí mismas no son significativas, entonces tener más pruebas ciertamente no indica un mejor software. El objetivo importante es asegurarse de que cada fragmento de código se pruebe, no solo se ejecute.

Lo que esto significa es que, si bien una cobertura baja significa que probablemente no estemos realizando suficientes pruebas, una cobertura alta por sí sola no necesariamente se correlaciona con una alta calidad. El panorama es más complicado que eso.

Trampa #1: “No conocemos nuestra cobertura”

No conocer tus coberturas me parece descabellado. Las herramientas de cobertura son baratas y abundantes. Un problema real que encuentran los equipos al intentar medir la cobertura es que el sistema es demasiado complicado. Cuando se crea una aplicación a partir de piezas superpuestas, saber dónde colocar los contadores de cobertura puede ser una tarea desalentadora. Yo sugeriría que si es difícil medir la cobertura en su aplicación, debería pensar dos veces acerca de la arquitectura.

Una segunda forma de caer en esta trampa ocurre con organizaciones que pueden tener muchas pruebas, pero no un número de cobertura real porque no tienen una herramienta o solución de cobertura de código que pueda agregar los números de diferentes ejecuciones de prueba. Si está realizando pruebas manuales, pruebas funcionales, pruebas unitarias y otros tipos, asegúrese de que la herramienta que está utilizando pueda agregar correctamente la cobertura para todos sus métodos de prueba.

En Parasoft, aprovechamos la gran cantidad de datos granulares capturados con la herramienta de análisis e informes Parasoft DTP, que proporciona una vista completa y agregada de la cobertura del código en contexto. Los monitores de aplicaciones recopilan datos de cobertura directamente de la aplicación mientras se prueba y luego envían esa información a DTP, que agrega datos de cobertura en todas las prácticas de prueba, equipos de prueba y ejecuciones de prueba.

Si eso parece una cantidad bastante importante de información, ¡tienes razón! DTP proporciona un panel interactivo para ayudarle a navegar por los datos y tomar decisiones sobre dónde centrar los esfuerzos de prueba. Vea el panel de ejemplo a continuación.

Captura de pantalla del panel del Centro de informes de Parasoft DTP que muestra análisis de cobertura de aplicaciones.

Si varias pruebas han cubierto el mismo código, no se contará en exceso. Las partes del código no probadas son rápidas y fáciles de ver. Esto le muestra qué partes de la aplicación han sido bien probadas y cuáles no.

Entonces, no más excusas para no medir la cobertura.

Trampa #2: “¡La cobertura lo es todo!”

Es común pensar erróneamente que la cobertura lo es todo. Una vez que los equipos pueden medir la cobertura, no es raro que los gerentes digan: "Aumentemos ese número". Con el tiempo, el número en sí se vuelve más importante que las pruebas. Quizás la mejor analogía sea la del fundador de Parasoft, Adam Kolawa:

“Es como pedirle a un pianista que cubra el 100% de las teclas del piano en lugar de presionar solo las teclas que tienen sentido en el contexto de una pieza musical determinada. Cuando toca la pieza, obtiene la cantidad de cobertura clave que tenga sentido ".

Ahí yace el problema. La cobertura sin sentido es lo mismo que la música sin sentido. La cobertura debe reflejar un uso real y significativo del código.

En determinadas industrias, como las críticas para la seguridad, por ejemplo, la métrica de cobertura del 100 % es un requisito. Pero incluso en ese caso, es muy fácil tratar cualquier ejecución de una línea de código como una prueba significativa, lo que puede no ser cierto. Para determinar si una prueba es una buena prueba, formule las siguientes dos preguntas básicas.

  1. ¿Qué significa cuando falla la prueba?
  2. ¿Qué significa cuando pasa la prueba?

Si no puede responder una de esas preguntas, probablemente tenga un problema con su prueba. Si no puede responder a ninguna de las dos preguntas, la prueba probablemente sea más problemática de lo que vale la pena. La manera de salir de esta trampa es, primero, comprender que el verdadero objetivo es crear pruebas útiles y significativas. La cobertura es importante. Mejorar la cobertura es un objetivo digno.

Ideas finales: la cobertura del código como camino hacia la excelencia en las pruebas

Si bien la cobertura del código es una métrica valiosa que puede ayudar a identificar partes del código no probadas y mejorar la calidad general de una base de código, debe considerarse como un aspecto de una estrategia de prueba más amplia. Incluso lograr una cobertura del código del 100% no significa necesariamente la ausencia de errores ni garantiza la corrección del software. Por sí solo no garantiza la excelencia en las pruebas.

Es una de varias prácticas que contribuyen a un enfoque de prueba integral. La cobertura del código puede ser parte de los esfuerzos de control de calidad, pero otros aspectos de las pruebas, como las pruebas unitarias, las pruebas de integración, las pruebas del sistema y las pruebas de aceptación del usuario, son igualmente importantes. La excelencia en las pruebas implica una combinación de varios tipos de pruebas, un buen diseño de pruebas, casos de prueba significativos y una evaluación y mejora continua del proceso de pruebas.

Cobertura integral del código: cobertura agregada en todas las prácticas de prueba