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 herramientas de diagnóstico (en su mayoría estáticas). En el tiempo de ejecución, sus pruebas no están a la vista. Son como los andamios que se usan para levantar un alto muro de ladrillos o una cúpula de roca. No deja partes importantes fuera de la estructura porque tiene un andamio que la sostiene durante la construcción. Usted tiene un andamio que lo sostiene durante la construcción para facilitar colocar todas las piezas importantes.
EDITAR: una analogía
¿Qué pasa con una analogía a los comentarios en código?
Los comentarios tienen su propósito, pero pueden ser redundantes o incluso dañinos. Por ejemplo, si pone conocimiento intrínseco sobre el código en comentarios , luego cambia el código, los comentarios se vuelven irrelevantes en el mejor de los casos y dañinos en el peor.
Entonces, digamos que usted pone mucho conocimiento intrínseco de su base de código en las pruebas, como que MethodA no puede tomar un nulo y el argumento de MethodB debe ser > 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í, debería 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í mismo, no intrínseco a las pruebas.
¿Qué podría salir mal?
El objetivo con respecto a las pruebas es idear todo lo que podría salir mal, escribir una prueba que verifique el comportamiento correcto, luego diseñar 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 acciona la programación defensiva, si las pruebas son exhaustivas.
Más pruebas, conduciendo más programación defensiva
Cuando los errores se encuentran inevitablemente, se escriben más pruebas para modelar las condiciones que manifiestan el error. Luego el código se corrige, con el código para hacer que se pasen las pruebas de esas , y las nuevas pruebas permanezcan en la suite de pruebas.
Un buen conjunto de pruebas pasará argumentos buenos y malos a una función / método y esperará resultados consistentes. Esto, a su vez, significa que el componente probado utilizará controles de condición previa (programación defensiva) para confirmar los argumentos que se le pasaron.
Hablando genéricamente ...
Por ejemplo, si un argumento nulo para un procedimiento en particular no es válido, entonces al menos una prueba pasará un nulo y esperará una excepción / error de "argumento nulo no válido" de algún tipo.
Por lo menos otra prueba pasará un argumento válido , por supuesto, o pasará a través de una gran matriz y pasará innumerables argumentos válidos, y confirmará que el estado resultante es adecuado.
Si un test no pasa ese argumento nulo y recibe una bofetada con la excepción esperada (y esa excepción fue lanzada porque el código verificó el estado pasado a la defensiva), entonces el nulo puede terminar asignados a una propiedad de una clase o enterrados 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 la clase, en algún lugar geográfico distante después de que el software se haya enviado . Y ese es el tipo de cosas que estamos tratando de evitar, ¿no?
Incluso podría ser peor. La instancia de clase con el estado no válido podría ser serializada y almacenada, solo para causar una falla cuando se reconstituye para su uso posterior. Caray, no sé, quizás sea un sistema de control mecánico de algún tipo que no se puede reiniciar después de un apagado porque no puede deserializar su propio estado de configuración persistente. O la instancia de la clase se podría serializar y pasar a un sistema completamente diferente creado por alguna otra entidad, y ese sistema podría fallar.
Especialmente si los programadores de ese otro sistema no codificaron a la defensiva.