La complejidad ciclomática es una métrica de software que se utiliza para indicar la complejidad de un programa . Es una medida cuantitativa del número de rutas linealmente independientes a través del código fuente de un programa . Fue desarrollada por Thomas J. McCabe, Sr. en 1976.
La complejidad ciclomática se calcula utilizando el grafo de flujo de control del programa. Los nodos del grafo corresponden a grupos indivisibles de comandos del programa, y una arista dirigida conecta dos nodos si el segundo comando puede ejecutarse inmediatamente después del primero. La complejidad ciclomática también puede aplicarse a funciones , módulos , métodos o clases individuales dentro de un programa.
Una estrategia de prueba , denominada prueba de ruta base por McCabe, quien la propuso por primera vez, consiste en probar cada ruta linealmente independiente a través del programa. En este caso, el número de casos de prueba será igual a la complejidad ciclomática del programa. [ 1 ]
Descripción
Definición

Hay varias formas de definir la complejidad ciclomática de una sección de código fuente . Una forma común es el número de rutas linealmente independientes dentro de ella. Un conjuntode caminos es linealmente independiente si el conjunto de aristas de cualquier caminoenno es la unión de conjuntos de aristas de los caminos en algún subconjunto deSi el código fuente no contiene instrucciones de control de flujo (condicionales o puntos de decisión), la complejidad sería 1, ya que solo habría una ruta a través del código. Si el código tuviera una instrucción IF de una sola condición , habría dos rutas a través del código: una donde la instrucción IF es VERDADERA y otra donde es FALSA. En este caso, la complejidad sería 2. Dos instrucciones IF anidadas de una sola condición, o una instrucción IF con dos condiciones, producirían una complejidad de 3.
Otra forma de definir la complejidad ciclomática de un programa es observar su grafo de flujo de control , un grafo dirigido que contiene los bloques básicos del programa, con una arista entre dos bloques básicos si el control puede pasar del primero al segundo. La complejidad M se define entonces como [ 2 ].
dónde
- E = el número de aristas del grafo.
- N = el número de nodos del grafo.
- P = el número de componentes conectadas .

Una formulación alternativa de esto, como se propuso originalmente, es usar un grafo en el que cada punto de salida está conectado de vuelta al punto de entrada. En este caso, el grafo es fuertemente conexo . Aquí, la complejidad ciclomática del programa es igual al número ciclomático de su grafo (también conocido como el primer número de Betti ), que se define como [ 2 ].
Esto puede interpretarse como el cálculo del número de ciclos linealmente independientes que existen en el grafo: aquellos ciclos que no contienen otros ciclos en su interior. Dado que cada punto de salida regresa al punto de entrada, existe al menos un ciclo de este tipo para cada punto de salida.
Para un solo programa (o subrutina o método), P siempre es igual a 1; una fórmula más simple para una sola subrutina es [ 3 ].
La complejidad ciclomática puede aplicarse a varios programas o subprogramas simultáneamente (por ejemplo, a todos los métodos de una clase). En estos casos, P será igual al número de programas en cuestión, y cada subprograma aparecerá como un subconjunto desconectado del grafo.
McCabe demostró que la complejidad ciclomática de un programa estructurado con un solo punto de entrada y un solo punto de salida es igual al número de puntos de decisión (sentencias "if" o bucles condicionales) contenidos en ese programa más uno. Esto es cierto solo para los puntos de decisión contados en las instrucciones de nivel de máquina más bajas. [ 4 ] Las decisiones que involucran predicados compuestos como los que se encuentran en lenguajes de alto nivel como IF cond1 AND cond2 THEN ...deben contarse en términos de las variables de predicado involucradas. En este ejemplo, se deben contar dos puntos de decisión porque a nivel de máquina es equivalente a IF cond1 THEN IF cond2 THEN .... [ 2 ] [ 5 ]
La complejidad ciclomática puede extenderse a un programa con múltiples puntos de salida. En este caso, es igual a dóndees el número de puntos de decisión en el programa y s es el número de puntos de salida. [ 5 ] [ 6 ]
Interpretación
En su presentación " Métricas de calidad de software para identificar riesgos" [ 7 ] para el Departamento de Seguridad Nacional, Tom McCabe introdujo la siguiente categorización de complejidad ciclomática:
- 1–10: Procedimiento sencillo, poco riesgo.
- 11–20: Más complejo, riesgo moderado
- 21–50: Complejo, de alto riesgo
- > 50: Código imposible de probar, riesgo muy alto
En topología algebraica
Un subgrafo par (también conocido como subgrafo euleriano ) es aquel en el que cada vértice incide en un número par de aristas. Estos subgrafos son uniones de ciclos y vértices aislados. Los subgrafos se identificarán mediante sus conjuntos de aristas, lo que equivale a considerar únicamente aquellos subgrafos pares que contienen todos los vértices del grafo completo.
El conjunto de todos los subgrafos pares de un grafo es cerrado bajo la diferencia simétrica y, por lo tanto, puede considerarse como un espacio vectorial sobre GF(2) . Este espacio vectorial se denomina espacio de ciclos del grafo. El número ciclomático del grafo se define como la dimensión de este espacio. Dado que GF(2) tiene dos elementos y el espacio de ciclos es necesariamente finito, el número ciclomático también es igual al logaritmo en base 2 del número de elementos en el espacio de ciclos.
Se construye fácilmente una base para el espacio de ciclos fijando primero un bosque generador del grafo y luego considerando los ciclos formados por una arista que no está en el bosque y el camino en el bosque que conecta los extremos de esa arista. Estos ciclos forman una base para el espacio de ciclos. El número ciclomático también es igual al número de aristas que no están en un bosque generador máximo de un grafo. Dado que el número de aristas en un bosque generador máximo de un grafo es igual al número de vértices menos el número de componentes, la fórmuladefine el número ciclomático. [ 8 ]
La complejidad ciclomática también puede definirse como un número de Betti relativo , el tamaño de un grupo de homología relativo :
que se lee como "el rango del primer grupo de homología del grafo G en relación con los nodos terminales t ". Esta es una forma técnica de decir "el número de caminos linealmente independientes a través del grafo de flujo desde una entrada hasta una salida", donde:
- "Linealmente independiente" corresponde a la homología, y el retroceso no se cuenta dos veces;
- "caminos" corresponde a la primera homología (un camino es un objeto unidimensional); y
- "Relativo" significa que el camino debe comenzar y terminar en un punto de entrada (o salida).
Esta complejidad ciclomática se puede calcular. También se puede calcular mediante el número de Betti absoluto identificando los nodos terminales en un componente dado, o dibujando caminos que conecten las salidas con la entrada. El nuevo grafo aumentadoobtiene
También se puede calcular mediante homotopía . Si un grafo de flujo de control (conectado) se considera un complejo CW unidimensional llamado, el grupo fundamental deserá. El valor dees la complejidad ciclomática. El grupo fundamental cuenta cuántos bucles hay a través del grafo hasta la homotopía, alineándose como se espera.
Aplicaciones
Limitar la complejidad durante el desarrollo
Una de las aplicaciones originales de McCabe fue limitar la complejidad de las rutinas durante el desarrollo de programas. Recomendó que los programadores calcularan la complejidad de los módulos que estaban desarrollando y los dividieran en módulos más pequeños cuando la complejidad ciclomática del módulo superara 10. [ 2 ] Esta práctica fue adoptada por la metodología de Pruebas Estructuradas del NIST , que observó que, desde la publicación original de McCabe, la cifra de 10 había recibido evidencia corroborativa sustancial. Sin embargo, también señaló que, en algunas circunstancias, podría ser apropiado flexibilizar la restricción y permitir módulos con una complejidad de hasta 15. Dado que la metodología reconoció que existían razones ocasionales para superar el límite acordado, formuló su recomendación como: «Para cada módulo, limite la complejidad ciclomática a [el límite acordado] o proporcione una explicación escrita de por qué se superó el límite». [ 9 ]
Medir la "estructuración" de un programa.
La sección VI del artículo de McCabe de 1976 trata sobre cómo son los grafos de flujo de control (GFC) de los programas no estructurados en términos de sus subgrafos, que McCabe identificó. (Para más detalles, véase el teorema del programa estructurado ). McCabe concluyó esa sección proponiendo una medida numérica de cuán cerca está un programa dado del ideal de programación estructurada, es decir, su "estructuralidad". McCabe denominó complejidad esencial a la medida que ideó para este propósito . [ 2 ]
Para calcular esta medida, el CFG original se reduce iterativamente identificando subgrafos con un único punto de entrada y salida, que luego se reemplazan por un único nodo. Esta reducción corresponde a lo que haría un humano si extrajera una subrutina del fragmento de código más grande. (Actualmente, este proceso se englobaría bajo el término general de refactorización ). El método de reducción de McCabe se denominó posteriormente condensación en algunos libros de texto, ya que se consideraba una generalización de la condensación a componentes utilizados en la teoría de grafos . [ 10 ] Si un programa está estructurado, el proceso de reducción/condensación de McCabe lo reduce a un único nodo CFG. Por el contrario, si el programa no está estructurado, el proceso iterativo identificará la parte irreducible. La medida de complejidad esencial definida por McCabe es simplemente la complejidad ciclomática de este grafo irreducible, por lo que será precisamente 1 para todos los programas estructurados, pero mayor que uno para los programas no estructurados. [ 9 ] : 80
Implicaciones para las pruebas de software
Otra aplicación de la complejidad ciclomática consiste en determinar el número de casos de prueba necesarios para lograr una cobertura de prueba exhaustiva de un módulo en particular.
Resulta útil debido a dos propiedades de la complejidad ciclomática, M , para un módulo específico:
- M es un límite superior para el número de casos de prueba necesarios para lograr una cobertura completa de las ramas .
- M es un límite inferior para el número de rutas a través del grafo de flujo de control (GFC). Suponiendo que cada caso de prueba toma una ruta, el número de casos necesarios para lograr la cobertura de rutas es igual al número de rutas que realmente se pueden tomar. Pero algunas rutas pueden ser imposibles, por lo que, si bien el número de rutas a través del GFC es claramente un límite superior para el número de casos de prueba necesarios para la cobertura de rutas, este último número (de rutas posibles ) a veces es menor que M.
Los tres números anteriores pueden ser iguales: cobertura de sucursalescomplejidad ciclomáticanúmero de caminos.
Por ejemplo, consideremos un programa que consta de dos sentencias if-then-else secuenciales.
si ( c1 ()) f1 (); de lo contrario f2 ();si ( c2 ()) f3 (); de lo contrario f4 ();
En este ejemplo, dos casos de prueba son suficientes para lograr una cobertura completa de ramas, mientras que cuatro son necesarios para una cobertura completa de rutas. La complejidad ciclomática del programa es 3 (ya que el grafo fuertemente conectado para el programa contiene 8 aristas, 7 nodos y 1 componente conectado) ( 8 − 7 + 2 ).
En general, para probar completamente un módulo, se deben probar todas las rutas de ejecución a través del mismo. Esto implica que un módulo con un número de complejidad alto requiere más esfuerzo de prueba que un módulo con un valor menor, ya que un número de complejidad más alto indica más rutas a través del código. Esto también implica que un módulo con mayor complejidad es más difícil de entender, ya que el programador debe comprender las diferentes rutas y los resultados de dichas rutas. [ 11 ] La complejidad ciclomática captura solo un aspecto del software, por lo que basarse únicamente en ella puede proporcionar una representación incompleta de la complejidad general de un programa.
Desafortunadamente, no siempre es práctico probar todas las rutas posibles en un programa. En el ejemplo anterior, cada vez que se agrega una instrucción if-then-else, el número de rutas posibles se duplica. A medida que el programa crece de esta manera, rápidamente llega un punto en el que probar todas las rutas se vuelve inviable.
Una estrategia de prueba común, defendida, por ejemplo, por la metodología de Pruebas Estructuradas del NIST, consiste en utilizar la complejidad ciclomática de un módulo para determinar el número de pruebas de caja blanca necesarias para obtener una cobertura suficiente del módulo. En casi todos los casos, según esta metodología, un módulo debería tener al menos tantas pruebas como su complejidad ciclomática. En la mayoría de los casos, este número de pruebas es suficiente para probar todas las rutas relevantes de la función. [ 9 ]
Como ejemplo de una función que requiere más que una simple cobertura de ramas para probarse con precisión, reconsideremos la función anterior. Sin embargo, supongamos que para evitar que se produzca un error, cualquier código que llame a una f1()u f3()otra función también debe llamar a la otra. [ a ] Suponiendo que los resultados de c1()ambas funciones c2()son independientes, la función presentada anteriormente contiene un error. La cobertura de ramas permite probar el método con solo dos pruebas, como los siguientes casos de prueba:
c1()devuelve verdadero yc2()devuelve verdaderoc1()devuelve falso yc2()devuelve falso
Ninguno de estos casos expone el error. Sin embargo, si utilizamos la complejidad ciclomática para indicar el número de pruebas que necesitamos, el número aumenta a 3. Por lo tanto, debemos probar una de las siguientes rutas:
c1()devuelve verdadero yc2()devuelve falsoc1()devuelve falso yc2()devuelve verdadero
Cualquiera de estas pruebas revelará el error.
Correlación con el número de defectos
Múltiples estudios han investigado la correlación entre el número de complejidad ciclomática de McCabe y la frecuencia de defectos que ocurren en una función o método. [ 12 ] Algunos estudios [ 13 ] encuentran una correlación positiva entre la complejidad ciclomática y los defectos; las funciones y los métodos que tienen la mayor complejidad tienden también a contener la mayor cantidad de defectos. Sin embargo, la correlación entre la complejidad ciclomática y el tamaño del programa (generalmente medido en líneas de código ) se ha demostrado muchas veces. [ 14 ] Les Hatton ha afirmado [ 15 ] que la complejidad tiene la misma capacidad predictiva que las líneas de código. Los estudios que controlaron el tamaño del programa (es decir, comparando módulos que tienen diferentes complejidades pero tamaño similar) son generalmente menos concluyentes, ya que muchos no encuentran una correlación significativa, mientras que otros sí la encuentran. Algunos investigadores cuestionan la validez de los métodos utilizados por los estudios que no encuentran correlación. [ 16 ] Aunque es probable que esta relación exista, no es fácil de usar en la práctica. [ 17 ] Dado que el tamaño del programa no es una característica controlable del software comercial , la utilidad del número de McCabe ha sido cuestionada. [ 12 ] La esencia de esta observación es que los programas más grandes tienden a ser más complejos y a tener más defectos. No se ha demostrado que reducir la complejidad ciclomática del código reduzca la cantidad de errores o fallos en dicho código. Sin embargo, las normas internacionales de seguridad, como la ISO 26262 , exigen directrices de codificación que recomiendan supervisar y procurar reducir la complejidad del código; cuando la complejidad es alta, se esperan medidas adicionales, incluido el escrutinio de las actividades de verificación y validación más difíciles , incluidas las pruebas. [ 18 ]
Véase también
Notas
- ↑ Este es un tipo de condición bastante común; considere la posibilidad de que
f1se asigne algún recurso quef3se libere.
Referencias
- ^ AJ Sobey. "Prueba de ruta básica" .
- 1 2 3 4 5 McCabe (diciembre de 1976). "Una medida de complejidad". IEEE Transactions on Software Engineering . SE-2 (4): 308–320 . Bibcode : 1976ITSEn...2..308M . doi : 10.1109/tse.1976.233837 . S2CID 9116234 .
- ↑ Philip A. Laplante (25 de abril de 2007). Lo que todo ingeniero debería saber sobre ingeniería de software . CRC Press. pág. 176. ISBN 978-1-4200-0674-2.
- ↑ Fricker, Sébastien (abril de 2018). "¿Qué es exactamente la complejidad ciclomática?" . froglogic GmbH . Consultado el 27 de octubre de 2018.
Para calcular una representación gráfica del código, podemos simplemente desensamblar su código ensamblador y crear un grafo siguiendo las siguientes reglas:
...
- 1 2 J. Belzer; A. Kent; AG Holzman; JG Williams (1992). Enciclopedia de Ciencias de la Computación y Tecnología . CRC Press. págs. 367–368 .
- ↑ Harrison (octubre de 1984). "Aplicación de la medida de complejidad de McCabe a programas de múltiples salidas". Software: Practice and Experience . 14 (10): 1004– 1007. doi : 10.1002/spe.4380141009 . S2CID 62422337 .
- ↑ Thomas McCabe Jr. (2008). "Métricas de calidad de software para identificar riesgos" . Archivado del original el 29 de marzo de 2022.
- ↑ Diestel, Reinhard (2000). Teoría de grafos . Textos de posgrado en matemáticas 173 (2.ª ed.). Nueva York: Springer. ISBN 978-0-387-98976-1.
- 1 2 3 Arthur H. Watson; Thomas J. McCabe (1996). "Pruebas estructuradas: una metodología de pruebas que utiliza la métrica de complejidad ciclomática" (PDF) . Publicación especial NIST 500-235.
- ↑ Paul C. Jorgensen (2002). Pruebas de software: Un enfoque artesanal, Segunda edición (2.ª ed.). CRC Press. págs. 150–153 . ISBN 978-0-8493-0809-3.
- ↑ Ebert, Christof; Cain, James; Antoniol, Giuliano; Counsell, Steve; Laplante, Phillip (2016). "Complejidad ciclomática". IEEE Software . 33 (6): 27– 29. Bibcode : 2016ISoft..33f..27E . doi : 10.1109/MS.2016.147 . ISSN 1937-4194 .
- 1 2 Norman E Fenton; Martin Neil (1999). "Una crítica de los modelos de predicción de defectos de software" (PDF) . IEEE Transactions on Software Engineering . 25 (3): 675– 689. Bibcode : 1999ITSEn..25..675F . CiteSeerX 10.1.1.548.2998 . doi : 10.1109/32.815326 .
- ↑ Schroeder, Mark (1999). "Una guía práctica para las métricas orientadas a objetos". IT Professional . 1 (6): 30– 36. Bibcode : 1999ITPro...1f..30S . doi : 10.1109/6294.806902 . S2CID 14945518 .
- ↑ Afriyie, Daniel; Labiche, Yvan (2021). "Predictores de la correlación de métricas de software: un análisis no paramétrico". 2021 IEEE 21.ª Conferencia Internacional sobre Calidad, Fiabilidad y Seguridad del Software (QRS) . págs. 524–533 . Bibcode : 2021qrs..conf...63A . doi : 10.1109/QRS54544.2021.00063 . ISBN 978-1-6654-5813-9.
- ↑ Les Hatton (2008). "El papel del empirismo en la mejora de la fiabilidad del software futuro" . Versión 1.1.
- ↑ Kan (2003). Métricas y modelos en ingeniería de calidad de software . Addison-Wesley. págs. 316–317 . ISBN 978-0-201-72915-3.
- ↑ GS Cherf (1992). "Una investigación sobre las características de mantenimiento y soporte del software comercial". Journal of Software Quality . 1 (3): 147– 158. doi : 10.1007/bf01720922 . ISSN 1573-1367 . S2CID 37274091 .
- ↑ ISO 26262-3:2011(en) Vehículos de carretera — Seguridad funcional — Parte 3: Fase conceptual . Organización Internacional de Normalización.
Enlaces externos
- Generación de métricas de complejidad ciclomática con Polyspace
- El papel del empirismo en la mejora de la fiabilidad del software futuro.
- La complejidad ciclomática de McCabe y por qué no la utilizamos.
- Métricas de software