Les tests sont là pour soutenir et assurer la programmation défensive
La programmation défensive protège l'intégrité du système au moment de l'exécution.
Les tests sont des outils de diagnostic (principalement statiques). Au moment de l'exécution, vos tests ne sont nulle part en vue. Ils sont comme les échafaudages utilisés pour ériger un haut mur de briques ou un dôme en pierre. Vous ne laissez pas de pièces importantes en dehors de la structure, car vous avez un échafaudage qui la retient pendant la construction. Vous avez un échafaudage qui le tient en place pendant la construction pour faciliter la mise en place de toutes les pièces importantes.
EDIT: une analogie
Qu'en est-il d'une analogie avec les commentaires dans le code?
Les commentaires ont leur raison d'être, mais peuvent être redondants, voire nuisibles. Par exemple, si vous mettez la connaissance intrinsèque sur le code dans les commentaires , puis modifiez le code, les commentaires deviennent au mieux inutiles et au pire nuisibles.
Donc, supposons que vous mettiez beaucoup de connaissances intrinsèques de votre base de code dans les tests, comme MethodA ne peut pas prendre une valeur nulle et l'argument de MethodB doit être > 0
. Ensuite, le code change. Null est acceptable pour A maintenant, et B peut prendre des valeurs aussi petites que -10. Les tests existants sont maintenant fonctionnellement faux, mais continueront à réussir.
Oui, vous devriez mettre à jour les tests en même temps que le code. Vous devez également mettre à jour (ou supprimer) les commentaires en même temps que le code. Mais nous savons tous que ces choses ne se produisent pas toujours et que des erreurs sont commises.
Les tests vérifient le comportement du système. Ce comportement réel est intrinsèque au système lui-même et non aux tests.
Qu'est ce qui pourrait aller mal?
L’objectif des tests est de penser à tout ce qui pourrait mal tourner, d’écrire un test qui vérifie le bon comportement, puis de créer le code d’exécution de sorte qu’il passe tous les tests.
Ce qui signifie que la programmation défensive est le point .
TDD pilote la programmation défensive, si les tests sont complets.
Plus de tests, conduisant à une programmation plus défensive
Lorsque des bogues sont inévitablement trouvés, davantage de tests sont écrits pour modéliser les conditions qui manifestent le bogue. Ensuite, le code est corrigé, avec le code nécessaire à la réussite de ces tests, et les nouveaux tests restent dans la suite de tests.
Un bon ensemble de tests va passer les bons et les mauvais arguments à une fonction / méthode et attendre des résultats cohérents. Cela signifie à son tour que le composant testé utilisera des contrôles de précondition (programmation défensive) pour confirmer les arguments qui lui sont transmis.
Généralement parlant ...
Par exemple, si un argument null dans une procédure particulière est invalide, alors au moins un test passera un null et attendra une exception / erreur "argument non valide".
Au moins un autre test va bien sûr passer un argument valide - ou parcourir un grand tableau et transmettre de nombreux arguments valides - et confirmer que l'état résultant est approprié.
Si un test ne réussit pas cet argument null et est mis en attente avec l'exception attendue (et que cette exception a été levée parce que le code a vérifié l'état de manière défensive), la valeur null peut être affectée à une propriété d'une classe ou être enterrée. dans une collection de quelque sorte où il ne devrait pas être.
Cela peut entraîner un comportement inattendu dans une partie du système totalement différente à laquelle l'instance de classe est transmise, dans des paramètres régionaux éloignés après la livraison du logiciel . Et c'est le genre de chose que nous essayons d'éviter, n'est-ce pas?
Cela pourrait même être pire. L'instance de classe avec l'état non valide pourrait être sérialisée et stockée, uniquement pour provoquer un échec lorsqu'elle sera reconstituée pour être utilisée ultérieurement. Bon sang, je ne sais pas, c'est peut-être un système de contrôle mécanique qui ne peut pas redémarrer après un arrêt, car il ne peut pas désérialiser son propre état de configuration persistant. Ou bien l'instance de classe peut être sérialisée et transmise à un système totalement différent créé par une autre entité et ce système peut tomber en panne.
Surtout si les programmeurs de cet autre système ne codaient pas de manière défensive.