2009-02-18 11 views
20

Solo he realizado pruebas menores de unidades en varios momentos de mi carrera. Cada vez que empiezo a bucear nuevamente, siempre me preocupa cómo demostrar que mis pruebas son correctas. ¿Cómo puedo saber que no hay un error en mi prueba de unidad? Por lo general, termino ejecutando la aplicación, comprobando que funciona, y luego usando la prueba unitaria como una especie de prueba de regresión. ¿Cuál es el enfoque recomendado y/o cuál es el enfoque que adoptas para este problema?¿Cómo dice que las pruebas de su unidad son correctas?

Editar: También me doy cuenta de que podría escribir pequeñas pruebas unitarias granulares que serían fáciles de entender. Sin embargo, si usted supone que el código pequeño y granular es impecable ya prueba de balas, puede escribir programas pequeños y granulares y no necesitar pruebas unitarias.

Edit2: Para los argumentos "la prueba unitaria es para asegurarse de que los cambios no rompan nada" y "esto solo ocurrirá si la prueba tiene el mismo defecto que el código", ¿y si la prueba se cubre? Es posible pasar el código bueno y el malo con una mala prueba. Mi pregunta principal es ¿qué tan buenas son las pruebas unitarias ya que si sus pruebas pueden ser defectuosas realmente no puede mejorar su confianza en su código, realmente no puede probar que su refactorización funcionó, y realmente no puede probar que cumplió con las especificaciones?

Respuesta

2

Supongo que escribir la prueba primero (antes de escribir el código) es una forma bastante buena de asegurarse de que la prueba sea válida.

O usted podría escribir pruebas para las pruebas unitarias ...: P

1

Nunca le cuentas. En general, las pruebas será más sencillo que el código que está probando, por lo que la idea es simplemente que van a ser menos propensos a tener errores que el código real hará.

+0

Una vez más, ¿por qué incluso escribir la unidad de prueba? ¿Por qué no simplemente escribir nuggets simples de código y afirmar que "los métodos son simples, por lo que tienen una pequeña posibilidad de tener errores" –

+0

Eso es lo que muchas personas intentan hacer. Sin embargo, a menos que seas un programador muy talentoso, a menudo es muy difícil escribir código tan simple y transparente. – mquander

0

Como anteriormente, la mejor manera es escribir la prueba antes de que el código real. Encuentre ejemplos de la vida real del código de su prueba también, si corresponde (fórmula matemática o similar), y compare la prueba de la unidad y el resultado esperado con eso.

7

Bueno, Dijkstra dijo la famosa frase:

"Las pruebas muestran la presencia, no el ausencia de errores"

OIA, ¿cómo escribir una prueba unitaria para la función add (int , En t)?

OIA, es una pregunta difícil.

+0

Ejemplo erróneo, no tan difícil. Hay INT_MIN, INT_MAX, -1, 0 y 1. Pruebe todas las permutaciones. ;-) – DevSolar

+0

@DevSolar: con sus entradas, ¿cómo se asegura de que add (42,13) ​​arroje el resultado correcto? Assaf no proporcionó el código fuente de su función y puede haber un caso específico con el valor 42. – mouviciel

+1

Se llama prueba de dominio. Para citar de http://www.testingeducation.org/BBST/Domain.html: la noción básica es que toma el gran espacio de posibles pruebas de una variable individual y la subdivide en subconjuntos que son (de alguna manera) equivalentes. Luego, prueba a un representante de cada subconjunto. – DevSolar

3

Para que esto sea un problema, su código debería tener errores de una manera que casualmente hace que sus pruebas pasen. Esto me sucedió recientemente, donde estaba comprobando que una condición dada (a) hacía que fallara un método. La prueba pasó (es decir, el método falló), pero pasó porque otra condición (b) provocó un error. Escriba sus pruebas cuidadosamente y asegúrese de que las pruebas de la unidad prueben UNA cosa.

general, sin embargo, las pruebas no se puede escribir para probar código es libre de errores. Son un paso en la dirección correcta.

+0

Tenga en cuenta mi comentario acerca de simplemente argumentar "escribir pruebas simples" No digo que las pruebas puedan demostrar que el código no tiene errores. Sin embargo, ¿cómo pueden aumentar mi confianza en mi código si las pruebas pueden ser erróneas? –

+0

No pueden darte plena confianza de que tu código está libre de errores. Sin embargo, pueden aumentar su confianza de que su código está libre de errores, como una cuestión de probabilidad. Ahora, para que su código tenga errores, sus pruebas deben exhibir el mismo comportamiento defectuoso que su código. –

1

Esto es algo que todos los errores que utiliza las pruebas unitarias. Si tuviera que darle una respuesta breve, le diría que siempre confíe en sus pruebas unitarias. Pero yo diría que esto debe ser respaldado con su experiencia previa:

  • ¿Ha tenido algún defecto que se informaron de las pruebas manuales y la prueba de la unidad no coger (aunque era responsable de) porque no había un error en tu prueba?
  • ¿Ha tenido falsos negativos en el pasado?
  • ¿Son las pruebas de su unidad lo suficientemente simples?
  • ¿Los escribes antes del código nuevo o al menos en paralelo?
15

La prueba de unidad debe expresar el "contrato" de lo que sea que esté probando. Es más o menos la especificación de la unidad puesta en el código. Como tal, dadas las especificaciones, debería ser más o menos obvio si las pruebas de la unidad son "correctas".

Pero no preocuparse demasiado acerca de la "corrección" de las pruebas unitarias. Son parte del software y, como tales, también podrían ser incorrectos. El punto de las pruebas unitarias, desde mi POV, es que aseguran que el "contrato" de su software no se haya roto por accidente. Eso es lo que hace que las pruebas unitarias sean tan valiosas: puedes cavar en el software, refactorizar algunas partes, cambiar los algoritmos en otros, y las pruebas de tu unidad te dirán si has roto algo. Incluso incorrecta pruebas de la unidad le dirá eso.

Si hay un error en las pruebas de su unidad, lo sabrá, porque la prueba de la unidad falla mientras que el código probado resulta ser correcto. Bueno, entonces, arregla la prueba unitaria. No es gran cosa.

+1

+1 para el argumento "test is specification". – mouviciel

+1

También encontré que un beneficio adicional de las pruebas como técnica de especificación es que puede hacer que el código de otras personas sea más fácil de entender (o viceversa). Ayuda a formar una intuición de lo que se supone que debe hacer el código. – rectangletangle

2
  1. El complejidad del código de prueba de unidad es (o debería ser) menos (a menudo decenas de veces menor) que el código real
  2. La posibilidad de que su codificación de un error en la prueba de unidad que coincide exactamente un error en su código real es mucho menos que solo codificar el error en su código real (si codifica un error en su prueba de unidad que no coincide con un error en su código real, debería fallar). Por supuesto, si ha hecho suposiciones incorrectas en su código real, es probable que vuelva a hacer la misma suposición, aunque el conjunto mental de pruebas unitarias aún debería reducir incluso ese caso
  3. Como ya se mencionó, cuando escribe una prueba unitaria tienen (o deberían tener) una mentalidad diferente. Al escribir código real estás pensando "cómo resuelvo este problema". Al escribir una prueba unitaria, usted está pensando: "¿Cómo pruebo cada posible forma de que esto se rompa?"

Como ya han dicho otros, no se trata de si puede probar que las pruebas de la unidad son correctas y completas (aunque es casi seguro que es mucho más fácil con el código de prueba), ya que reduce el número de errores a un número muy bajo y lo empuja cada vez más hacia abajo.

Por supuesto, tiene que llegar un punto en el que su confianza en su unidad pruebe lo suficiente como para confiar en ellos, por ejemplo, al hacer refactorizaciones. Llegar a este punto suele ser solo un caso de experiencia e intuición (aunque existen herramientas de cobertura de código que ayudan).

1

No se puede probar pruebas son correctas, y si usted está tratando, que estás haciendo mal.

Las pruebas unitarias son una primera pantalla - una prueba de humo - como todas las pruebas automatizadas. Están principalmente allí para decirle si un cambio que hace más tarde en rompe cosas. No están diseñados para ser una prueba de calidad, incluso con una cobertura del 100%.

La métrica hace que la gestión de sentirse mejor, sin embargo, y esto es útil en sí mismo a veces!

7

Hay dos maneras para ayudar a garantizar la exactitud de las pruebas unitarias:

  • TDD: escribir la prueba primero, y luego escribir el código que está destinado a probar. Eso significa que llega a ver fallar. Si sabe que detecta al menos algunas clases de errores (como "No he implementado ninguna funcionalidad en la función que deseo probar todavía"), entonces sabe que no es completamente inútil. Todavía puede dejar pasar otros errores, pero sabemos que la prueba no es completamente incorrecta.
  • Tienen muchas pruebas. Si una prueba permite que algunos errores pasen desapercibidos, lo más probable es que causen errores más adelante en la línea, lo que ocasionará que otras pruebas fallen. Cuando lo note y arregle el código ofensivo, tendrá la oportunidad de examinar por qué la primera prueba no detectó el error como se esperaba.

Y, por último, por supuesto, mantenga las pruebas unitarias tan simples que es poco probable que contengan errores.

2

Primero permítanme comenzar diciendo que las pruebas unitarias NO son solo sobre pruebas. Se trata más sobre el diseño de la aplicación. Para ver esto en acción, debe colocar una cámara con su pantalla y registrar su codificación mientras escribe las pruebas de la unidad. Se dará cuenta de que está tomando muchas decisiones de diseño al escribir pruebas unitarias.

¿Cómo saber si las pruebas de mi unidad son buenas?

¡No puede probar el período de piezas lógicas! Si su código dice que 2 + 2 = 5 y su prueba se está asegurando de que 2 + 2 = 5, entonces para usted 2 + 2 es 5. Para escribir buenas pruebas de unidad, DEBE tener una buena comprensión del dominio con el que está trabajando. Cuando sepa lo que está tratando de lograr, redactará buenas pruebas y un buen código para lograrlo. Si tiene muchas pruebas de unidad y sus suposiciones son incorrectas, tarde o temprano descubrirá sus errores.

1

Esta es una de las ventajas de TDD: el código actúa como una prueba para las pruebas.

Es posible que cometa errores equivalentes, pero es poco común en mi experiencia.

Pero ciertamente he tenido el caso donde escribo una prueba que debería fallar solo para que pase, lo que me indica que mi prueba fue incorrecta.

Cuando estaba por primera vez aprendiendo pruebas unitarias, y antes de hacer TDD, también deliberadamente rompía el código después de escribir la prueba para asegurar que fallara como esperaba. Cuando no sabía, la prueba estaba rota.

Me gusta mucho la descripción de Bob Martin de que es equivalente a la contabilidad de doble entrada.

3

tenía esta misma pregunta, y después de leer los comentarios, esto es lo que ahora pienso (con el debido crédito a las respuestas anteriores):

creo que el problema puede ser que los dos nos tomamos el propósito aparente de la unidad pruebas - para probar que el código es correcto - y aplicado ese propósito a las pruebas mismas. Está bien hasta donde llegue, excepto el propósito de las pruebas unitarias no es demostrar que el código es correcto.

Al igual que con todos los esfuerzos no triviales, nunca puede estar 100% seguro. El propósito correcto de las pruebas unitarias es reducir los errores, no eliminarlos.Más específicamente, como otros han notado, cuando haces cambios más tarde eso podría romper algo accidentalmente. Las pruebas unitarias son solo una herramienta para reducir errores, y ciertamente no deberían ser las únicas. Idealmente, combina las pruebas unitarias con la revisión del código y el control de calidad sólido para reducir los errores a un nivel tolerable.

Las pruebas de unidad son mucho más simples que su código; no es posible hacer que su código sea tan simple como una prueba de unidad si su código hace algo significativo. Si escribe un código "pequeño, granular" que es fácil de probar correcto, entonces su código consistirá en una gran cantidad de funciones pequeñas, y aún tendrá que determinar si todas funcionan correctamente en concierto.

Como las pruebas unitarias son inevitablemente más simples que el código que están probando, es menos probable que tengan errores. Incluso si algunas de las pruebas de su unidad tienen errores, en general, mejorarán la calidad de su base de código principal. (Si las pruebas de su unidad son tan defectuosas que esto no es cierto, entonces es probable que su base de código principal también sea una pila humeante, y está completamente jodido. Creo que todos asumimos un nivel básico de competencia.)

Si NECESITA aplicar un segundo nivel de pruebas unitarias para comprobar que sus pruebas unitarias son correctas, puede hacerlo, pero está sujeto a rendimientos decrecientes. Para verlo de manera falsa numérica:

Suponga que las pruebas unitarias reducen el número de errores de producción en un 50%. Luego, escribe pruebas de metaunidades (pruebas unitarias para encontrar errores en las pruebas unitarias). Diga que esto tiene problemas con las pruebas de su unidad, lo que reduce la tasa de errores de producción al 40%. Pero llevó 80% de tiempo escribir las pruebas de metaunidad como lo hizo escribir las pruebas unitarias. Para el 80% del esfuerzo, solo obtuviste otro 20% de la ganancia. Tal vez escribir pruebas de meta-meta-unidades te da otros 5 puntos porcentuales, pero ahora nuevamente tomó el 80% del tiempo que tomó escribir las pruebas de la unidad meta, por lo que el 64% del esfuerzo de escribir las pruebas unitarias (que 50%) obtuviste otro 5%. Incluso con números sustancialmente más liberales, no es una forma eficiente de pasar su tiempo.

En este escenario, está claro que pasar el punto de escribir pruebas unitarias no vale la pena el esfuerzo.

0

Editar: También me doy cuenta de que podría escribir pequeñas pruebas unitarias granulares que serían fáciles de entender. Sin embargo, si usted supone que el código pequeño y granular es impecable ya prueba de balas, puede escribir programas pequeños y granulares y no necesitar pruebas unitarias.

La idea de la prueba unitaria es probar las cosas más granulares, luego apilar las pruebas para probar el caso más grande. Si está escribiendo pruebas grandes, pierde un poco de los beneficios allí, aunque es probable que sea más rápido escribir pruebas más grandes.

+0

Acepto que las pruebas unitarias deben probar pequeñas piezas individuales de funcionalidad. Sin embargo, no me gustó el argumento de que las pruebas podrían considerarse correctas ya que eran pequeñas y granulares y parecen ser correctas. Si ese fuera el caso, podría escribir código granular pequeño, verificar que se vea correcto y luego no escribir pruebas unitarias. –

1

Dominic mencionó que "Para que esto sea un problema, su código debería tener errores de una manera que casualmente hace que sus pruebas pasen". Una técnica que puede usar para ver si esto es un problema es mutation testing. Hace cambios a su código y ve si hace que las pruebas de la unidad fallen. Si no lo hace, puede indicar áreas donde las pruebas no son 100% exhaustivas.

1

Pruebas de la unidad son sus requisitos concretized. No sé ustedes, pero me gusta tener los requisitos especificados antes de comenzar a codificar (TDD). Al escribirlos y tratarlos como cualquier otra parte de su código, comenzará a sentirse seguro presentando nuevas funciones sin romper la funcionalidad anterior. Para asegurarse de que se necesita todo su código y de que las pruebas realmente prueban el código, uso pitest (existen otras variantes para las pruebas de mutación para otros idiomas). Para mí, código no probado, es un código defectuoso, por limpio que sea.

Si la prueba prueba un código complejo y es compleja, a menudo escribo pruebas para mis pruebas (example).

Cuestiones relacionadas