Las pruebas están ahí para apoyar y garantizar la programación defensiva.
La programación defensiva protege la integridad del sistema en tiempo de ejecución.
Las pruebas son (en su mayoría estáticas) herramientas de diagnóstico. En tiempo de ejecución, sus pruebas no están a la vista. Son como andamios utilizados para levantar una pared de ladrillo o una cúpula de roca. No deja partes importantes fuera de la estructura porque tiene un andamio que lo sostiene durante la construcción. Tiene un andamio sosteniéndolo durante la construcción para facilitar la colocación de todas las piezas importantes.
EDITAR: una analogía
¿Qué pasa con una analogía con los comentarios en código?
Los comentarios tienen su propósito, pero pueden ser redundantes o incluso perjudiciales. Por ejemplo, si pone conocimiento intrínseco sobre el código en los comentarios , luego cambia el código, los comentarios se vuelven irrelevantes en el mejor de los casos y dañinos en el peor.
Supongamos que pone mucho conocimiento intrínseco de su base de código en las pruebas, como el Método A no puede ser nulo y el argumento del Método B debe serlo > 0
. Entonces el código cambia. Nulo está bien para A ahora, y B puede tomar valores tan pequeños como -10. Las pruebas existentes ahora son funcionalmente incorrectas, pero continuarán pasando.
Sí, debe actualizar las pruebas al mismo tiempo que actualiza el código. También debe actualizar (o eliminar) los comentarios al mismo tiempo que actualiza el código. Pero todos sabemos que estas cosas no siempre suceden y que se cometen errores.
Las pruebas verifican el comportamiento del sistema. Ese comportamiento real es intrínseco al sistema en sí, no intrínseco a las pruebas.
¿Qué podría salir mal?
El objetivo con respecto a las pruebas es pensar en todo lo que podría salir mal, escribir una prueba que verifique el comportamiento correcto, luego elaborar el código de tiempo de ejecución para que pase todas las pruebas.
Lo que significa que la programación defensiva es el punto .
TDD impulsa la programación defensiva, si las pruebas son exhaustivas.
Más pruebas, más programación defensiva
Cuando inevitablemente se encuentran errores, se escriben más pruebas para modelar las condiciones que manifiestan el error. Luego, el código se corrige, con código para hacer que esas pruebas pasen, y las nuevas pruebas permanecen en el conjunto de pruebas.
Un buen conjunto de pruebas va a pasar argumentos buenos y malos a una función / método, y esperar resultados consistentes. Esto, a su vez, significa que el componente probado usará verificaciones de precondición (programación defensiva) para confirmar los argumentos que se le pasan.
Hablando genéricamente ...
Por ejemplo, si un argumento nulo para un procedimiento en particular es inválido, entonces al menos una prueba pasará un nulo, y esperará una excepción / error de "argumento nulo inválido" de algún tipo.
Al menos otra prueba va a pasar un argumento válido , por supuesto, o pasar por una gran matriz y pasar innumerables argumentos válidos, y confirmar que el estado resultante es apropiado.
Si una prueba no pasa ese argumento nulo y recibe una bofetada con la excepción esperada (y esa excepción se lanzó porque el código verificó defensivamente el estado que se le pasó), entonces el nulo puede terminar asignado a una propiedad de una clase o enterrado en una colección de algún tipo donde no debería estar.
Esto podría causar un comportamiento inesperado en una parte completamente diferente del sistema a la que se pasa la instancia de clase, en una ubicación geográfica distante después de que se haya enviado el software . Y ese es el tipo de cosas que realmente estamos tratando de evitar, ¿verdad?
Incluso podría ser peor. La instancia de clase con el estado no válido podría serializarse y almacenarse, solo para causar un error cuando se reconstituya para usarse más adelante. Dios, no lo sé, tal vez es un sistema de control mecánico de algún tipo que no puede reiniciarse después de un apagado porque no puede deserializar su propio estado de configuración persistente. O la instancia de clase se puede serializar y pasar a un sistema completamente diferente creado por otra entidad, y ese sistema podría fallar.
Especialmente si los programadores de ese otro sistema no codificaron defensivamente.