2009-09-25 25 views
31

¿Sus pruebas unitarias constituyen una cobertura de código del 100%? Sí o no, y por qué o por qué no.Cobertura del código de prueba unitaria: ¿tiene una cobertura del 100%?

+4

No puedo decir que alguna vez haya reservado un vuelo para * realmente * probar mi código ... :-P –

+2

Debe consultar esta pregunta: http://stackoverflow.com/questions/90002/what-is- a-reasonable-code-coverage-for-unit-tests-and-why/90021 –

Respuesta

13

Raramente es práctico obtener una cobertura de código del 100% en un sistema no trivial. La mayoría de los desarrolladores que escriben pruebas unitarias disparan entre mediados y finales de los 90.

Una herramienta de prueba automatizada como Pex puede ayudar a aumentar la cobertura del código. Funciona mediante la búsqueda de casos extremos difíciles de encontrar.

+0

+1 Bien dicho :) –

+1

El problema al hacerlo es que el 10% de código difícil de probar es también el código no trivial que contiene 90% del error! Esta es la conclusión que obtuve empíricamente después de muchos años de TDD. –

13

No, porque no es una solución de compromiso entre la práctica las pruebas unitarias perfectos y realmente terminar un proyecto :)

3

No porque pasé mi tiempo la adición de nuevas características que ayudan a los usuarios en lugar de difícil de escribir pruebas oscuros que entregar poco valor. Yo digo prueba unitaria las cosas grandes, las cosas sutiles y las cosas que son frágiles.

45

No por varias razones:

  • Es muy caro para alcanzar la cobertura del 100%, en comparación con el 90% o 95% por un beneficio que no es obvia.
  • Incluso con el 100% de cobertura, su código es no perfecto. Echar un vistazo a este método (de hecho, depende de qué tipo de cobertura que está hablando - cobertura de sucursales, cobertura de línea ...):


public static String foo(boolean someCondition) { 
    String bar = null; 
    if (someCondition) { 
     bar = "blabla"; 
    } 
    return bar.trim(); 
} 

y la prueba unitaria:

assertEquals("blabla", foo(true)); 

La prueba tendrá éxito, y la cobertura de su código es del 100%. Sin embargo, si se agrega otra prueba:

assertEquals("blabla", foo(false)); 

por lo que recibirá un NullPointerException. ¡Y como estuvo al 100% con la primera prueba, no necesariamente habría escrito la segunda!

En general, considero que el código crítica debe estar cubierto en casi el 100%, mientras que el otro código puede estar cubierto al 85-90%

+10

+1 para afirmar que la cobertura de código 100% no implica un conjunto de pruebas perfecto. Necesitará una cobertura de ruta del 100%, que es extremadamente difícil (e imposible en muchos casos). – Falaina

+2

Está hablando de cobertura de función, una medida de si se invocan todas las funciones del programa durante la prueba. Esperaría que esta métrica sea 100% en todos los casos; ¿Cómo podría confiar en un conjunto de pruebas que no llamó a todas las funciones de su código al menos una vez? –

+6

¡No estoy hablando de cobertura de funciones aquí! En mi ejemplo, la primera prueba unitaria brinda una cobertura del 100% de * línea * cobertura * función *. Sin embargo, como afirma Falaina, la cobertura * ruta * no es 100% aquí (lo cual es extremadamente difícil de conseguir), y es por eso que la segunda prueba fallará, incluso si ya obtengo una cobertura de línea * 100% con la primera prueba ... – romaintaz

4

personalmente encuentro cobertura de la prueba 100% para ser problemático en múltiples niveles En primer lugar, debe asegurarse de obtener un beneficio tangible y económico de las pruebas de unidad que redacta. Además, las pruebas unitarias, como cualquier otro código, son CÓDIGO. Eso significa que, al igual que cualquier otro código, debe verificarse para su corrección y mantenerse. Ese tiempo adicional para verificar la corrección del código adicional, y mantenerlo y mantener esas pruebas válidas en respuesta a los cambios en el código comercial, agrega un costo. Lograr una cobertura del 100% de las pruebas y asegurarse de que prueba su código lo más exhaustivamente posible es un esfuerzo encomiable, pero lograrlo a cualquier costo ... bueno, a menudo es demasiado costoso.

Muchas veces, cuando se cubren los errores y las comprobaciones de validez para cubrir casos marginales o extremadamente raros, pero definitivamente posibles, los casos excepcionales son un ejemplo de código que no necesariamente debe cubrirse.La cantidad de tiempo , esfuerzo (y en última instancia dinero) que debe ser invertido para lograr la cobertura de esos casos raros marginales es a menudo un desperdicio a la luz de otras necesidades de negocio. Las propiedades son a menudo parte del código que, especialmente con C# 3.0, no necesitan ser probadas ya que la mayoría de las propiedades, si no todas, se comportan exactamente de la misma manera y son excesivamente simples (retorno o conjunto de declaración única). Invertir cantidades enormes de las pruebas de unidad de envoltura de tiempo alrededor de miles de propiedades bien podría invertirse mejor en otro lugar donde se pueda obtener un retorno tangible mayor y más valioso de esa inversión.

Más allá de simplemente lograr una cobertura de prueba del 100%, existen problemas similares al tratar de configurar la unidad "perfecta". Los marcos burlones han progresado a un grado sorprendente en estos días, y casi cualquier cosa puede ser burlada (si estás dispuesto a pagar dinero, TypeMock puede burlarse de cualquier cosa y todo, pero cuesta mucho). Sin embargo, a menudo hay momentos en que las dependencias de su código no se escribieron de manera simulada (esto es en realidad un problema central con la gran mayoría del framework .NET). Invertir tiempo para lograr el alcance adecuado de una prueba es útil, pero poner cantidades excesivas de Es hora de burlarse de todo y de cualquier cosa bajo la luz del sol, al agregar capas de abstracción e interfaces para hacerlo posible, nuevamente es más una pérdida de tiempo, esfuerzo y finalmente dinero.

El objetivo final con las pruebas no debería ser lograr la máxima cobertura de código. El objetivo final debe ser lograr el mayor valor por unidad de tiempo invertido en escribir pruebas unitarias, mientras cubre tanto como sea posible en ese momento. La mejor manera de lograr esto es tomar el enfoque de BDD: (. El comportamiento ... no unidad) Especificar sus preocupaciones, definir su contexto y verificar los resultados esperados ocurren para cualquier pieza de comportamiento siendo desarrollado

2

Generalmente escribo pruebas unitarias solo como un método de prevención de regresión. Cuando se informa que tengo un error que corregir, creo una prueba de unidad para asegurarme de que no vuelva a aparecer en el futuro. Puedo crear algunas pruebas para las secciones de funcionalidad que tengo para asegurarme de que permanezca intacto (o para interacciones complejas entre las partes), pero generalmente quiero que la corrección de errores me diga que es necesario.

10

Sí, lo hacemos.

Depende del lenguaje y el marco que esté utilizando en cuanto a lo fácil que es lograrlo.

Estamos usando Ruby on Rails para mi proyecto actual. Ruby es muy "mockable" en que puedes tropezar grandes trozos de tu código sin tener que construir diseños de construcción y composición de clase demasiado complicados que tendrías que hacer en otros idiomas.

Dicho esto, solo tenemos el 100% de la cobertura de la línea (básicamente, lo que rcov le brinda). Aún debes pensar en probar todas las ramas requeridas.

Esto solo es posible si lo incluye desde el principio como parte de su construcción de integración continua, y rompe la compilación si la cobertura cae por debajo del 100%, lo que obliga a los desarrolladores a solucionarlo inmediatamente.Por supuesto, podría elegir otro número como objetivo, pero si está empezando de nuevo, no hay mucha diferencia para el esfuerzo de obtener del 90% al 100%

También tenemos un montón de otros Las métricas que rompen la construcción si también cruzan un umbral dado (complejidad ciclomática, duplicación, por ejemplo), todas van juntas y ayudan a reforzarse mutuamente.

De nuevo, realmente tiene que tener esto en su lugar desde el principio para seguir trabajando en un nivel estricto, ya sea eso o establecer un objetivo al que pueda golpear, y gradualmente aumentarlo hasta llegar al nivel que está feliz con.

¿Esto le agrega valor? Al principio era escéptico, pero honestamente puedo decir que sí. No principalmente porque ha probado el código (aunque definitivamente es un beneficio), sino más bien en términos de escribir código simple que es fácil de probar y razonar. Si sabe que tiene para tener una cobertura de prueba del 100%, deja de escribir demasiado complejo si/else/while/try/catch monstrosities y Keep It Simple Stupid.

+0

"Si sabes que tienes que tener una cobertura de prueba del 100%, dejas de escribir demasiado complejo si/else/while/try/catch monstrosities" - Punto muy interesante. – funroll

1

Solo tengo 100% de cobertura en las piezas nuevas de código que se han escrito con la capacidad de prueba en mente. Con la encapsulación adecuada, cada clase y función puede tener funcional pruebas de unidad que ofrecen simultáneamente una cobertura cercana al 100%. Es solo cuestión de agregar algunas pruebas adicionales que cubren algunos casos extremos para que llegue al 100%.

No debe escribir pruebas solo para obtener cobertura. Debería escribir pruebas funcionales que prueben la corrección/cumplimiento. Con una buena especificación funcional que cubre todos los motivos y un buen diseño de software, puede obtener una buena cobertura de forma gratuita.

1

Sí, he tenido proyectos que han tenido una cobertura de línea del 100%. Véase mi respuesta a un semejante question.

Usted puede obtener el 100% línea de cobertura, pero, como otros han señalado aquí en la SO y en otros lugares en el Internet es quizás tan sólo un mínimo. Cuando considera la cobertura de rutas y sucursales, hay mucho más trabajo por hacer.

La otra forma de verlo es tratar de hacer que su código sea tan simple que sea fácil obtener una cobertura de línea del 100%.

3

Por lo general, logro llegar a 93 .. 100% con mi cobertura pero ya no aspiro al 100%. Solía ​​hacer eso y aunque es factible, no vale la pena el esfuerzo más allá de un cierto punto, porque las pruebas ciegamente obvias generalmente no son necesarias. Buen ejemplo de esto podría ser la rama verdadera evaluación del siguiente código cortó

public void method(boolean someBoolean) { 
    if (someBoolean) { 
     return; 
    } else { 
     /* do lots of stuff */ 
    } 
} 

Sin embargo lo que es importante lograr es lo más cercano a una cobertura del 100% en funcionales partes de la clase como sea posible ya que esos son los peligrosos aguas de su aplicación, el pantano neblinoso de insectos rastreros y comportamiento indefinido y, por supuesto, el circo de pulgas que hace dinero.

+1

¿Es necesaria la cláusula else? –

+0

No, solo para enfatizar lo que estoy buscando. De hecho, si esto fuera código de producción, no lo habría agregado allí. – Esko

+0

"no vale la pena el esfuerzo más allá de cierto punto porque las pruebas ciegamente obvias generalmente no son necesarias" - el código obviamente obvio llevaría literalmente segundos para escribir una prueba, por lo que "no vale la pena el esfuerzo" es un argumento que cae plano. –

8

Lo que hago cuando tengo la oportunidad es insertar instrucciones en cada rama del código que se puede analizar y registrar si han sido afectadas, de modo que pueda hacer algún tipo de comparación para ver qué declaraciones no han sido golpeados Esto es un poco pesado, así que no siempre soy bueno al respecto.

Acabo de construir una pequeña aplicación de interfaz de usuario para utilizar en subastas de caridad, que utiliza MySQL como su base de datos.Como realmente no quería romper en medio de una subasta, intenté algo nuevo.

ya que estaba en VC6 (C++ + MFC) que define dos macros:

#define TCOV ASSERT(FALSE) 
#define _COV ASSERT(TRUE) 

y luego me roció

TCOV; 

a través del código, en cada trayectoria separada que pude encontrar, y en cada rutina Luego ejecuté el programa bajo el depurador, y cada vez que llegaba a TCOV, se detenía. Me gustaría ver el código de cualquier problema obvio, y luego editarlo en _COV, luego continuar. El código se recompilaría sobre la marcha y pasaría al siguiente TCOV. De esta manera, lentamente, laboriosamente, eliminé suficientes declaraciones TCOV para que se ejecutara "normalmente".

Después de un tiempo, copié el código para TCOV, y eso mostró qué código no había probado. Luego volví y lo volví a ejecutar, asegurándome de probar más ramas que no había probado antes. Seguí haciendo esto hasta que no hubo TCOV declaraciones en el código.

Esto llevó unas pocas horas, pero en el proceso encontré y solucioné varios errores. No hay manera de que pudiera tener la disciplina para hacer y seguir un plan de prueba que hubiera sido tan exhaustivo. No solo sabía que había cubierto todas las ramas, sino que me había hecho ver en cada rama mientras estaba en funcionamiento, un muy buen tipo de revisión de código.

Por lo tanto, utilice o no una herramienta de cobertura, esta es una buena manera de eliminar los errores que de lo contrario acecharían en el código hasta un momento más embarazoso.

+0

¿Es esto algo que se te ocurrió? Parece que la técnica podría funcionar con un nombre. – funroll

+0

@funroll: Nombre? Solo lo considero como una prueba de cobertura. ¿Tienes alguna idea? –

+0

Me gusta este método, voy a tratar de hacer esto como una forma de prueba de ramificación –

3

De Ted Neward blog.

En este punto en el tiempo, la mayoría de los desarrolladores han escuchado al menos, si no se considera la adopción de, el meme de prueba masoquista. Compañeros de NFJS Stuart Halloway y Justin Gehtland han fundado una empresa de consultoría, Relevance, que establece un estándar alto como estándar cultural corporativo: cobertura 100% de prueba de su código. Neal Ford ha informado que ThoughtWorks hace declaraciones similares, aunque entiendo que los clientes a veces ponen obstáculos accidentales en su camino para lograr dicha meta. Es amistoso, pero como dice el antiguo proverbio indio americano, si apuntas tu flecha al sol, volará más alto y padre que si lo apuntas al suelo.

26

A todo el comprobador de cobertura del 90%:

El problema con ello es que el 10% difícil de probar el código es también el código no trivial que contiene el 90% del error! Esta es la conclusión que obtuve empíricamente después de muchos años de TDD.

Y después de todo esto es una conclusión bastante directa. Este 10% de código difícil de probar, es difícil de probar porque refleja un problema de negocios engañoso o un defecto de diseño complicado o ambos. Estas razones exactas que a menudo conducen a un código defectuoso.

Pero también:

  • 100% código cubierto que disminuye con el tiempo a menos de 100% cubierto menudo señala un fallo, o al menos un defecto.
  • Código 100% cubierto que se usa junto con contratos, es el arma definitiva para llevar a vivir cerca del código libre de errores. Code Contracts and Automated Testing are pretty much the same thing
  • Cuando se descubre un error en el código cubierto al 100%, es más fácil de arreglar. Dado que el código responsable del error ya está cubierto por las pruebas, no debería ser difícil escribir nuevas pruebas para cubrir la corrección de errores.
4

En un nuevo proyecto practico TDD y mantengo una cobertura de línea del 100%. En su mayoría ocurre de forma natural a través de TDD. Las lagunas de cobertura generalmente merecen la atención y se satisfacen fácilmente. Si la herramienta de cobertura que estoy usando brinda cobertura de sucursales u otra cosa, le prestaré atención, aunque nunca he visto la cobertura de sucursales decirme nada, probablemente porque TDD llegó primero.

Mi argumento más fuerte para mantener una cobertura del 100% (si se preocupan por la cobertura en todos) es que que es mucho más fácil mantener una cobertura del 100% de cobertura para gestionar inferior al 100%. Si tiene una cobertura del 100% y se cae, inmediatamente sabe por qué y puede solucionarlo fácilmente, porque la caída está en el código en el que acaba de trabajar. Pero si se conforma con el 95% o lo que sea, es fácil pasar por alto las regresiones de cobertura y siempre volverá a revisar los vacíos conocidos. Es la razón exacta por la cual las mejores prácticas actuales requieren que el conjunto de pruebas de uno pase por completo. Cualquier cosa menos es más difícil, no más fácil de manejar.

Mi actitud definitivamente se ve reforzada por haber trabajado en Ruby durante algún tiempo, donde hay excelentes marcos de prueba y dobles de prueba son fáciles. La cobertura del 100% también es fácil en Python. Puede que tenga que reducir mis estándares en un entorno con herramientas menos susceptibles.

Me encantaría tener los mismos estándares en proyectos heredados, pero nunca me ha parecido práctico llevar una gran aplicación con cobertura mediocre hasta el 100% de cobertura; Tuve que conformarme con el 95-99%. Siempre ha sido demasiado trabajo para volver atrás y cubrir todo el código anterior. Esto no contradice mi argumento de que es fácil mantener una base de código al 100%; es mucho más fácil cuando mantienes ese estándar desde el principio.

0

En muchos casos no vale la pena obtener el 100% de la cobertura de extracto, pero en algunos casos, es vale la pena. En algunos casos, la cobertura del extracto del 100% es demasiado lax un requisito.

La pregunta clave es "¿cuál es el impacto si el software falla (produce el resultado incorrecto)?". En la mayoría de los casos, el impacto de un error es relativamente bajo. Por ejemplo, tal vez tenga que arreglar el código dentro de unos días y volver a ejecutar algo. Sin embargo, si el impacto es "alguien puede morir en 120 segundos", entonces ese es un gran impacto, y usted debe tener un lote más cobertura de prueba que solo el 100% de la cobertura de extracto.

Dirijo el Core Infrastructure Initiative Best Practices Badge para Linux Foundation. Nosotros do tenemos una cobertura de estado del 100%, pero no diría que fue estrictamente necesario. Durante mucho tiempo estuvimos muy cerca del 100%, y decidimos hacer ese último porcentaje. Sin embargo, no pudimos justificar el último por ciento en terrenos de ingeniería; esos últimos por ciento fueron agregados puramente como "orgullo de la mano de obra". Me da un poco de tranquilidad tener una cobertura del 100%, pero realmente no era necesario. Teníamos más del 90% de cobertura de estado solo por las pruebas normales, y eso estuvo bien para nuestros propósitos.Dicho esto, queremos que el software sea sólido como una roca, y tener una cobertura de estado del 100% nos ha ayudado a llegar allí. También es más fácil obtener una cobertura del estado del 100% hoy.

Aún es útil para medir la cobertura, incluso si no necesita el 100%. Si sus pruebas no tienen una cobertura decente, usted se debe preocupar por. Un conjunto de pruebas incorrecto puede tener una buena cobertura de declaración, pero si no tiene una buena cobertura de declaración, entonces, por definición, tiene un conjunto de pruebas incorrecto. Lo que necesita es una compensación: ¿cuáles son los riesgos (probabilidad e impacto) del software que no se han probado? Por definición, es más probable que tenga errores (¡no lo probó!), Pero si usted y sus usuarios pueden vivir con esos riesgos (probabilidad e impacto), está bien. Para muchos proyectos de menor impacto, creo que la cobertura del estado de cuenta del 80% -90% está bien, con mejor ser mejor.

Por otro lado, si las personas pueden morir por errores en su software, la cobertura del 100% de las declaraciones no es suficiente. Al menos agregaría una cobertura de sucursal, y tal vez más, para verificar la calidad de sus pruebas. Las normas como DO-178C (para sistemas aerotransportados) adoptan este enfoque: si una falla es menor, no es gran cosa, pero si una falla puede ser catastrófica, entonces se requieren pruebas mucho más rigurosas. Por ejemplo, DO-178C requiere MC/DC coverage para el software más crítico (el software que puede matar rápidamente a las personas si comete un error). MC/DC es mucho más extenuante que la cobertura de extracto o incluso la cobertura de sucursal.

Cuestiones relacionadas