unit testing - test - Errores de la cobertura del código
live unit testing (13)
Estoy buscando ejemplos del mundo real de algunos efectos secundarios negativos de la cobertura del código.
Noté que esto sucedía en el trabajo recientemente debido a una política para lograr una cobertura de código del 100%. La calidad del código ha mejorado con seguridad, pero a la inversa, los evaluadores parecen estar escribiendo planes de prueba más laxos porque "bueno, el código está totalmente probado por unidad". Como resultado, algunos errores lógicos lograron pasar. Fueron un DOLOR REALMENTE GRANDE para depurar porque "bueno, el código está totalmente probado en unidades".
Creo que eso se debió en parte a que nuestra herramienta solo cubría las declaraciones. Aún así, podría haber sido mejor tiempo invertido.
Si alguien tiene otros efectos secundarios negativos de tener una póliza de cobertura de código, por favor comparta. Me gustaría saber qué tipo de otros "problemas" están ocurriendo en el mundo real.
Gracias por adelantado.
EDIT: Gracias por todas las respuestas realmente buenas. Hay algunos que marcaría como la respuesta, pero solo puedo marcar uno por desgracia.
- Escribir casos de prueba demasiado específicos.
- Insuficiente prueba de variabilidad de entrada del Código.
- Gran cantidad de casos de prueba artificiales ejecutados.
- No concentrarse en las fallas de prueba importantes debido al ruido.
- Dificultad para asignar defectos porque muchas condiciones de muchos componentes deben interactuar para que se ejecute una línea.
El peor efecto secundario de tener un objetivo de cobertura del 100% es pasar gran parte del ciclo de desarrollo de las pruebas (75% +) aprovechando los casos de esquina. Otro efecto negativo de dicha política es la concentración de golpear una línea de código en particular en lugar de abordar el rango de entradas. Realmente no me importa que la función strcpy se haya ejecutado al menos una vez. Realmente me importa que funcione contra una gran variedad de entradas. Tener una política es bueno. Pero tener una política extremadamente draconiana es malo. La métrica del 100% de la cobertura del código no es necesaria ni suficiente para que el código se considere sólido.
! 00% de cobertura de código significa que un código bien probado es un mito completo. Como desarrolladores conocemos las partes difíciles / complejas / delicadas de un sistema, y preferiría ver esas áreas adecuadamente probadas, y solo obtener una cobertura del 50%, en lugar de la cifra sin sentido de que cada línea se ha ejecutado al menos una vez.
En términos de un ejemplo del mundo real, el único equipo en el que estuve que tenía una cobertura del 100% escribió uno de los peores códigos que he visto en mi vida. Se utilizó una cobertura del 100% para reemplazar la revisión del código: el resultado fue terriblemente malo, en la medida en que se descartó la mayoría de los códigos, aunque pasaron las pruebas.
A veces, los casos de esquina son tan raros que no vale la pena probarlos, sin embargo, una regla de cobertura de código estricta requiere que la pruebes de todos modos.
Por ejemplo, en Java el algoritmo MD5 está incorporado, pero técnicamente es posible que se genere una excepción de tipo "algoritmo no soportado". Nunca se lanza y su prueba debería pasar por giros significativos para probar ese camino.
Sería mucho trabajo perdido.
El hecho de que haya cobertura de código no significa que esté probando todas las rutas a través de la función.
Por ejemplo, este código tiene cuatro caminos:
if (A) { ... } else { ... }
if (B) { ... } else { ... }
Sin embargo, solo dos pruebas (por ejemplo, una con A y B verdaderas, una con A y B falsas) darían "cobertura de código al 100%".
Esto es un problema porque la tendencia es dejar de probar una vez que hayas alcanzado el número mágico del 100%.
En mi experiencia, el mayor problema con las herramientas de cobertura de código es el riesgo de que alguien sea víctima de la creencia de que "alta cobertura de código" es igual a "buenas pruebas". La mayoría de las herramientas de cobertura solo ofrecen métricas de cobertura de estados de cuenta, a diferencia de la condición, la ruta de datos o la cobertura de decisiones. Eso significa que es posible obtener una cobertura del 100% con un código como este:
for (int i = 0; i < MAX_RETRIES; ++i) {
if (someFunction() == MAGIC_NUMBER) {
break;
}
}
... sin siquiera probar la condición de terminación en el ciclo for.
Peor aún, es posible obtener una "cobertura" muy alta de una prueba que simplemente invoca su aplicación, sin molestarse en validar la salida o validarla incorrectamente.
En pocas palabras, los bajos niveles de cobertura del código son sin duda una indicación de pruebas insuficientes, pero los altos niveles de cobertura no son indicativos de una prueba suficiente o correcta.
En mi opinión, el mayor peligro que corre un equipo al medir la cobertura del código es que premia las pruebas grandes y penaliza a las pequeñas. Si puede elegir entre escribir una sola prueba que cubra una gran parte de la funcionalidad de su aplicación y escribir diez pruebas pequeñas que prueban un solo método, solo la medición de la cobertura del código implica que debe escribir la prueba grande.
Sin embargo, escribir el conjunto de 10 pruebas pequeñas le dará pruebas menos frágiles y pondrá a prueba su aplicación mucho más a fondo que una prueba grande. Por lo tanto, al medir la cobertura del código, particularmente en una organización con hábitos de prueba en evolución, a menudo puede configurar los incentivos incorrectos.
En una oración: la cobertura del código te dice lo que definitivamente no has probado, no lo que tienes.
Parte de la construcción de un conjunto de pruebas de unidades valiosas es encontrar el código de alto riesgo más importante y formularle preguntas difíciles. Quieres asegurarte de que las cosas difíciles funcionen como una prioridad. Las cifras de cobertura no tienen ninguna noción de la "importancia" del código ni de la calidad de las pruebas.
En mi experiencia, muchas de las pruebas más importantes que jamás escribirá son las que apenas agregan cobertura (casos de borde que agregan un poco de% adicional aquí y allá, pero encuentran muchos errores).
El problema con establecer objetivos de cobertura difíciles y (potencialmente contraproducentes) es que los desarrolladores pueden tener que comenzar a hacer lo imposible para probar su código. Se está haciendo el código comprobable, y luego solo hay tortura. Si alcanzas el 100% de cobertura con excelentes pruebas, eso es fantástico, pero en la mayoría de las situaciones el esfuerzo adicional no vale la pena.
Además, las personas comienzan a obsesionarse / jugar con los números en lugar de centrarse en la calidad de las pruebas. He visto pruebas mal escritas que tienen más del 90% de cobertura, al igual que he visto pruebas excelentes que solo tienen una cobertura del 60-70%.
De nuevo, tiendo a considerar la cobertura como un indicador de lo que definitivamente no se ha probado.
Existen herramientas, por así Jumble , que realizan análisis a través de la cobertura de sucursales, al mutar su código para ver si su prueba falla para todas las permutaciones diferentes.
Directamente desde su sitio web:
Jumble es una herramienta de prueba de mutación de nivel de clase que funciona en conjunto con JUnit. El objetivo de las pruebas de mutación es proporcionar una medida de la efectividad de los casos de prueba. Se realiza una única mutación en el código que se probará, luego se ejecutan los casos de prueba correspondientes. Si el código modificado no supera las pruebas, esto aumenta la confianza en las pruebas. Por el contrario, si el código modificado pasa las pruebas, esto indica una deficiencia de prueba.
La cobertura de código 100% no significa que haya terminado con las pruebas usnit
function int divide(int a, int b) {
return a/b;
}
Con solo 1 prueba de unidad, obtengo una cobertura de código del 100% para esta función:
return divide(4,2) == 2;
Ahora, nadie podría argumentar que este código de unidad con cobertura del 100% indica que la característica funciona bien.
Creo que la cobertura de código es un buen elemento para saber si te falta una ruta de código obvia, pero la usaría con cuidado.
No hay nada malo con la cobertura del código: lo que veo mal es la cifra del 100%. En algún momento, la ley de rendimientos disminuidos entra en acción y resulta más costoso probar el último 1% que el otro 99%. La cobertura del código es una meta digna, pero el sentido común es muy útil.
Sé que esta no es una respuesta directa a tu pregunta, pero ...
Cualquier prueba, independientemente de qué tipo, es insuficiente por sí misma. Pruebas unitarias / cobertura de código es para desarrolladores. QA todavía necesita probar el sistema como un todo. Los usuarios de negocios también necesitan probar el sistema en conjunto.
Por el contrario, QA prueba el código completamente, por lo que los desarrolladores no deberían probarlo es igual de malo. Las pruebas son complementarias y diferentes pruebas proporcionan diferentes cosas. Cada tipo de prueba puede perder cosas que otro podría encontrar.
Al igual que el resto del desarrollo, no tome atajos con las pruebas, solo dejará pasar los errores.
Tenemos buenas herramientas para medir la cobertura de código a partir de pruebas unitarias. Por lo tanto, es tentador confiar en la cobertura del código del 100% para representar que usted está "haciendo pruebas". Esto no es verdad.
Como han mencionado otras personas, la cobertura del 100% del código no prueba que usted haya probado adecuadamente, ni el 50% de la cobertura del código significa necesariamente que no ha probado adecuadamente.
La medición de líneas de código ejecutadas por pruebas es solo una métrica. También debe probar una variedad razonable de entradas de función, y también cómo se comporta la función o clase dependiendo de algún otro estado externo. Por ejemplo, algunos códigos funcionan de forma diferente según los datos de una base de datos o de un archivo.
También he blogueado sobre esto recientemente: http://karwin.blogspot.com/2009/02/unit-test-coverage.html
Una de las mayores dificultades de la cobertura del código es que la gente solo habla de la cobertura del código sin especificar realmente de qué tipo de código están hablando. Las características de C0, C1, C2 e incluso niveles más altos de cobertura de código son muy diferentes, por lo que hablar de "cobertura de código" no tiene sentido.
Por ejemplo, lograr una cobertura de ruta completa al 100% es prácticamente imposible. Si su programa tiene n
puntos de decisión, necesita 2 n pruebas (y dependiendo de la definición, cada bit de un valor es un punto de decisión, de modo de lograr una cobertura de ruta 100% completa para una función extremadamente simple que solo agrega dos int
s , necesita 18446744073709551616 pruebas). Si solo tiene un ciclo, ya necesita infinitas pruebas.
OTOH, lograr una cobertura del 100% C0 es trivial.
Otra cosa importante a recordar, es que la cobertura del código no le dice qué código se probó. ¡Solo te dice qué código se ejecutó ! Puede probarlo usted mismo: tome una base de código que tenga una cobertura de código del 100%. Elimine todas las afirmaciones de las pruebas. Ahora el código base todavía tiene una cobertura del 100%, ¡pero no prueba una sola cosa! Entonces, la cobertura del código no le dice qué se prueba, solo qué no se prueba.