Recién salí de la universidad y comenzaré la universidad en algún lugar la próxima semana. Hemos visto pruebas unitarias, pero no las hemos usado mucho; y todos hablan de ellos, así que pensé que tal vez debería hacer algo.
El problema es que no sé qué probar. ¿Debo probar el caso común? El caso del borde? ¿Cómo sé que una función está cubierta adecuadamente?
Siempre tengo la terrible sensación de que, si bien una prueba demostrará que una función funciona para un caso determinado, es completamente inútil demostrar que la función funciona, punto.
Respuestas:
Mi filosofía personal ha sido hasta ahora:
fuente
Entre la gran cantidad de respuestas hasta ahora, nadie ha tocado el reparto de equivalencia y el análisis del valor límite , consideraciones vitales en la respuesta a la pregunta en cuestión. Todas las otras respuestas, aunque útiles, son cualitativas, pero es posible, y preferible, ser cuantitativas aquí. @fishtoaster proporciona algunas pautas concretas, solo asomándose bajo las coberturas de la cuantificación de la prueba, pero la división de equivalencia y el análisis del valor límite nos permiten hacerlo mejor.
En la partición de equivalencia , divide el conjunto de todas las entradas posibles en grupos según los resultados esperados. Cualquier entrada de un grupo producirá resultados equivalentes, por lo tanto, dichos grupos se denominan clases de equivalencia . (Tenga en cuenta que los resultados equivalentes no significan resultados idénticos).
Como un ejemplo simple, considere un programa que debería transformar los caracteres ASCII en minúsculas en caracteres en mayúsculas. Otros personajes deben sufrir una transformación de identidad, es decir, permanecer sin cambios. Aquí hay un posible desglose en clases de equivalencia:
La última columna informa el número de casos de prueba si los enumera todos. Técnicamente, según la regla 1 de @ fishtoaster, incluiría 52 casos de prueba; todos los de las dos primeras filas indicadas anteriormente se incluyen en el "caso común". La regla 2 de @ fishtoaster agregaría también algunas o todas las filas 3 y 4 anteriores. Sin embargo, con la partición de equivalencia someter a prueba toda una caso de prueba en cada clase de equivalencia es suficiente. Si elige "a" o "g" o "w", está probando la misma ruta de código. Por lo tanto, tiene un total de 4 casos de prueba en lugar de 52+.
El análisis del valor límite recomienda un ligero refinamiento: esencialmente sugiere que no todos los miembros de una clase de equivalencia son, bueno, equivalentes. Es decir, los valores en los límites también deben considerarse dignos de un caso de prueba por derecho propio. (¡Una justificación fácil para esto es el infame error off-by-one !) Por lo tanto, para cada clase de equivalencia podría tener 3 entradas de prueba. Mirando el dominio de entrada anterior, y con cierto conocimiento de los valores ASCII, podría encontrar estas entradas de casos de prueba:
(Tan pronto como obtenga más de 3 valores límite que sugieran que tal vez quiera repensar sus delimitaciones de clase de equivalencia originales, pero esto fue lo suficientemente simple como para que no volviera a revisarlos). Por lo tanto, el análisis del valor límite nos lleva a solo 17 casos de prueba, con una alta confianza de cobertura completa, en comparación con 128 casos de prueba para realizar pruebas exhaustivas. (¡Sin mencionar que la combinatoria dicta que las pruebas exhaustivas son simplemente inviables para cualquier aplicación del mundo real!)
fuente
Probablemente mi opinión no es muy popular. Pero le sugiero que sea económico con las pruebas unitarias. Si tiene demasiadas pruebas unitarias, puede terminar fácilmente pasando la mitad de su tiempo o más con el mantenimiento de las pruebas en lugar de la codificación real.
Le sugiero que escriba pruebas para cosas que tiene un mal presentimiento o cosas que son cruciales y / o elementales. Las pruebas unitarias de la OMI no son un reemplazo para una buena ingeniería y codificación defensiva. Actualmente trabajo en un proyecto que es más o menos inutilizable. Es realmente estable pero un dolor para refactorizar. De hecho, nadie ha tocado este código en un año y la pila de software en la que se basa tiene 4 años. ¿Por qué? Porque está abarrotado de pruebas unitarias, para ser precisos: pruebas unitarias y pruebas de integración automatizadas. (¿Alguna vez has oído hablar de pepino y cosas similares?) Y esta es la mejor parte: este (aún) inestimable software ha sido desarrollado por una compañía cuyos empleados son pioneros en la escena del desarrollo basado en pruebas. :RE
Entonces mi sugerencia es:
Comience a escribir pruebas después de desarrollar el esqueleto básico; de lo contrario, la refactorización puede ser dolorosa. Como desarrollador que desarrolla para otros, nunca obtienes los requisitos desde el principio.
Asegúrese de que sus pruebas unitarias se puedan realizar rápidamente. Si tiene pruebas de integración (como pepino), está bien si tardan un poco más. Pero las pruebas de larga duración no son divertidas, créeme. (La gente olvida todas las razones por las cuales C ++ se ha vuelto menos popular ...)
Deje estas cosas TDD a los expertos TDD.
Y sí, a veces te concentras en los casos extremos, a veces en los casos comunes, dependiendo de dónde esperes lo inesperado. Aunque si siempre espera lo inesperado, debería repensar su flujo de trabajo y disciplina. ;-)
fuente
Leave this TDD stuff to the TDD-experts
.Si está probando primero con Test Driven Development, entonces su cobertura estará en el rango del 90% o más, porque no agregará funcionalidad sin primero escribir una prueba de unidad fallida.
Si está agregando pruebas después del hecho, entonces no puedo recomendar lo suficiente como para que obtenga una copia de Working Effectively With Legacy Code de Michael Feathers y eche un vistazo a algunas de las técnicas para agregar pruebas a su código y formas de refactorizar su código para hacerlo más comprobable.
fuente
The problem is, I don't know _what_ to test
Si comienza a seguir las prácticas de desarrollo guiado por pruebas , lo guiarán a través del proceso y sabrá qué probar será algo natural. Algunos lugares para comenzar:
Las pruebas son lo primero
Nunca, nunca escriba código antes de escribir las pruebas. Ver Red-Green-Refactor-Repeat para una explicación.
Escribir pruebas de regresión
Cada vez que encuentre un error, escriba un caso de prueba y asegúrese de que falle . A menos que pueda reproducir un error a través de un caso de prueba fallido, realmente no lo ha encontrado.
Rojo-Verde-Refactor-Repetir
Rojo : comience escribiendo una prueba más básica para el comportamiento que está tratando de implementar. Piense en este paso como escribir un código de ejemplo que use la clase o función en la que está trabajando. Asegúrese de que compila / no tiene errores de sintaxis y que falla . Esto debería ser obvio: no ha escrito ningún código, por lo que debe fallar, ¿verdad? Lo importante que debe aprender aquí es que, a menos que vea que la prueba falla al menos una vez, nunca puede estar seguro de que si pasa, lo hace debido a algo que ha hecho por alguna razón falsa.
Verde : escriba el código más simple y estúpido que realmente hace pasar la prueba. No trates de ser inteligente. Incluso si ve que hay un caso límite obvio pero la prueba tiene en cuenta, no escriba código para manejarlo (pero no olvide el caso límite: lo necesitará más adelante). La idea es que cada pieza de código que escriba, cada
if
, cadatry: ... except: ...
debe estar justificada por un caso de prueba. El código no tiene que ser elegante, rápido u optimizado. Solo quieres que la prueba pase.Refactor : Limpie su código, obtenga los nombres de método correctos. Vea si la prueba todavía está pasando. Optimizar. Ejecute la prueba nuevamente.
Repita : recuerda el caso límite que la prueba no cubrió, ¿verdad? Entonces, ahora es su gran momento. Escriba un caso de prueba que cubra esa situación, vea cómo falla, escriba un código, vea cómo pasa, refactorice.
Prueba tu código
Estás trabajando en un código específico, y esto es exactamente lo que quieres probar. Esto significa que no debe probar las funciones de la biblioteca, la biblioteca estándar o su compilador. Además, trate de evitar probar el "mundo". Esto incluye: llamar a API web externas, algunas cosas intensivas en bases de datos, etc. Siempre que pueda intentar simularlo (cree un objeto que siga la misma interfaz, pero que devuelva datos estáticos predefinidos).
fuente
Para las pruebas unitarias, comience con la prueba de que hace lo que está diseñado para hacer. Ese debería ser el primer caso que escriba. Si parte del diseño es "debería arrojar una excepción si pasa basura", pruébelo también, ya que es parte del diseño.
Comience con eso. A medida que adquiera experiencia con la realización de las pruebas más básicas, comenzará a aprender si eso es suficiente o no, y comenzará a ver otros aspectos de su código que necesitan pruebas.
fuente
La respuesta común es "probar todo lo que pueda romperse" .
¿Qué es demasiado simple para romper? Campos de datos, accesos de propiedad con muerte cerebral y gastos generales similares. Cualquier otra cosa probablemente implementa alguna parte identificable de un requisito, y puede beneficiarse de la prueba.
Por supuesto, su kilometraje y las prácticas de su entorno de trabajo pueden variar.
fuente