Bon, je réponds ici, mais mon commentaire donne mon avis général sur la question des tests (ce qui peut plus ou moins recouper des informations d'autres commentaires plus bas).
Premièrement, avoir une couverture de test de 100% c'est super difficile, surtout avec un langage objet, puisqu'une méthode peut réagir de différentes manières selon l'état de l'objet. Essayer de couvrir tout le code d'une classe pour chaque état de l'objet reviendrait à explorer un arbre de possibilité infinie (ou du moins qui s'agrandit exponentiellement).
Deuxièmement, un test n'a selon moi un intêret que s'il est écrit avant le code qu'il doit tester cela afin d'éviter les biais logique du programmeur-testeur. Je trouve ça plus logique qu'on code une fonction à partir des spécifs (dont les tests peuvent faire partie) plutôt que l'inverse. C'est ce que préconise d'ailleurs la méthode XP.
Troisièmement, pour rejoindre ce qui est dit plus bas, si tu veux une preuve de la fiabilité de ton programme il faut utiliser des méthodes formelles. Personnelement je trouve les langages à spécifications formelles (Coq, PWS, B, Z ...) fastidieux et surtout très eloignées des connaissances actuelles des programmeurs (surtout niveau mathématique). Je suis plutôt partisant de l'ajout de formalisme simple dans les programmes, comme par exemple le propose Eiffel avec ses pre/postcondition et invariant (on trouve des équivalents dans les langages plus communs comme Java). Cela permet en deux mots de spécifier plus formellement ce qu'une méthode est censé faire (=~postcondition) d'après ce qu'on lui passe comme paramètres (=~precondition). On peut voir ça comme une signature sémantique de la méthode (à comparer à la signature syntaxique classique (type de retour, type des paramètres)).
En utilisant un système de test avec des contrats, on peut arriver à gagné un peu de confiance dans son application. Sauf que rien ne dit qu'on a bien écrit nos tests ou nos contrats (je ne parle même pas du code:). J'ai pu pendant mes études tester un outil assez sympas pour justement faire des "tests" de "robustesse" de tout cela. Il s'agit de générer des mutants (déformation de la sémantique du code, par exemple remplacer un + par un -) dans le code et de lancer les tests dessus. À l'execution, si tes tests et contrats sont bien écris ils devraients détecter les erreurs, dans le cas contraire c'est qu'ils sont mal définis. Pour en revenir à la couverture des tests, on se rends compte avec cette méthode qu'on détecte rapidement les endroits non testés : ce sont les endroits où le plus de mutants restent vivants (le programme continue de s'executer ou s'est planté).
Le problème, c'est que je ne suis pas sûr que les techniques d'analyse de code par mutation soient sortit des labos de recherche :/. J'avais personnelement essayé JMutator de l'équipe Triskell de l'IRISA à Rennes. C'est dommage, j'avais trouvé ça plutôt puissant même si c'était un peu long (pour avoir des données significatives il faut générer beaucoup de mutants, et dans JMutator chaque mutant correspond à une nouvelle compilation d'une classe Java).
Pour conclure, il n'existe pas de méthodes miracles, c'est à toi de trouver la solution correspond le mieux à tes besoins.
[^] # Re: Pas si simple...
Posté par pifou . En réponse au journal couverture de code. Évalué à 3.
Premièrement, avoir une couverture de test de 100% c'est super difficile, surtout avec un langage objet, puisqu'une méthode peut réagir de différentes manières selon l'état de l'objet. Essayer de couvrir tout le code d'une classe pour chaque état de l'objet reviendrait à explorer un arbre de possibilité infinie (ou du moins qui s'agrandit exponentiellement).
Deuxièmement, un test n'a selon moi un intêret que s'il est écrit avant le code qu'il doit tester cela afin d'éviter les biais logique du programmeur-testeur. Je trouve ça plus logique qu'on code une fonction à partir des spécifs (dont les tests peuvent faire partie) plutôt que l'inverse. C'est ce que préconise d'ailleurs la méthode XP.
Troisièmement, pour rejoindre ce qui est dit plus bas, si tu veux une preuve de la fiabilité de ton programme il faut utiliser des méthodes formelles. Personnelement je trouve les langages à spécifications formelles (Coq, PWS, B, Z ...) fastidieux et surtout très eloignées des connaissances actuelles des programmeurs (surtout niveau mathématique). Je suis plutôt partisant de l'ajout de formalisme simple dans les programmes, comme par exemple le propose Eiffel avec ses pre/postcondition et invariant (on trouve des équivalents dans les langages plus communs comme Java). Cela permet en deux mots de spécifier plus formellement ce qu'une méthode est censé faire (=~postcondition) d'après ce qu'on lui passe comme paramètres (=~precondition). On peut voir ça comme une signature sémantique de la méthode (à comparer à la signature syntaxique classique (type de retour, type des paramètres)).
En utilisant un système de test avec des contrats, on peut arriver à gagné un peu de confiance dans son application. Sauf que rien ne dit qu'on a bien écrit nos tests ou nos contrats (je ne parle même pas du code:). J'ai pu pendant mes études tester un outil assez sympas pour justement faire des "tests" de "robustesse" de tout cela. Il s'agit de générer des mutants (déformation de la sémantique du code, par exemple remplacer un + par un -) dans le code et de lancer les tests dessus. À l'execution, si tes tests et contrats sont bien écris ils devraients détecter les erreurs, dans le cas contraire c'est qu'ils sont mal définis. Pour en revenir à la couverture des tests, on se rends compte avec cette méthode qu'on détecte rapidement les endroits non testés : ce sont les endroits où le plus de mutants restent vivants (le programme continue de s'executer ou s'est planté).
Le problème, c'est que je ne suis pas sûr que les techniques d'analyse de code par mutation soient sortit des labos de recherche :/. J'avais personnelement essayé JMutator de l'équipe Triskell de l'IRISA à Rennes. C'est dommage, j'avais trouvé ça plutôt puissant même si c'était un peu long (pour avoir des données significatives il faut générer beaucoup de mutants, et dans JMutator chaque mutant correspond à une nouvelle compilation d'une classe Java).
Pour conclure, il n'existe pas de méthodes miracles, c'est à toi de trouver la solution correspond le mieux à tes besoins.