2010-08-20 7 views
9

La méthode suivante ne doit être appelée que s'il a été vérifié qu'il y a des chiffres invalides (en appelant une autre méthode). Comment puis-je tester le throw-line dans l'extrait de code suivant? Je sais que l'one-way pourrait être de fusionner ensemble le VerifyThereAreInvalidiDigits et cette méthode. Je cherche d'autres idées. Je voudrais également ne pas écrire un test unitaire qui exerce un code qui ne devrait jamais être exécuté.Comment tester un code qui ne devrait jamais être exécuté?

+1

Le simple fait que vous le dites ne devrait pas être exécuté et ne peut pas être atteint me dit "Retirez-le". Vous avez validé la chaîne comme étant invalide pour arriver ici, donc vous savez que la propriété va retourner quelque chose. – Michael

Répondre

22

Si le « jeter » la déclaration en question est vraiment injoignable dans tout scénario possible, alors il devrait être supprimé et remplacé par:

Debug.Fail("This should be unreachable; please find and fix the bug that caused this to be reached."); 

Si le code est accessible alors écrire un test d'unité teste ce scénario. Les scénarios de rapport d'erreurs pour les méthodes accessibles au public sont des scénarios parfaitement valides. Vous devez gérer toutes les entrées correctement, même les mauvaises entrées. Si la bonne chose à faire est de lancer une exception, alors testez que vous lancez une exception. MISE À JOUR: selon les commentaires, il est en fait impossible de toucher l'erreur et donc le code est inaccessible. Mais maintenant le Debug.Fail n'est pas accessible non plus, et il ne compile pas parce que le compilateur note qu'une méthode qui retourne une valeur a un point d'extrémité atteignable.

Le premier problème ne devrait pas réellement être un problème; L'outil de couverture de code doit sûrement être configurable pour ignorer le code de débogage inaccessible. Mais à la fois problème peut être résolu par la réécriture de la boucle:

public int FirstInvalidDigitPosition 
{ 
    get 
    { 
     int index = 0; 
     while(true) 
     { 
      Debug.Assert(index < this.positions.Length, "Attempt to get invalid digit position but there are no invalid digits!"); 
      if (!this.positions[index].Valid) return index; 
      index++; 
     } 
    } 
} 

Une autre approche serait de réorganiser le code de sorte que vous n'avez pas le problème en premier lieu:

public int? FirstInvalidDigitPosition { 
    get { 
     for (int index = 0; index < this.positions.Count; ++index) { 
      if (!this.positions[index].Valid) return index; 
     } 
     return null; 
    } 
} 

et maintenant vous n'avez pas besoin de restreindre les appelants pour appeler AreThereInvalidDigits en premier; Il est juste légal d'appeler cette méthode à tout moment. Cela semble être la chose la plus sûre à faire. Les méthodes qui explosent lorsque vous ne faites pas de vérification coûteuse pour vérifier qu'elles sont sûres d'appeler sont des méthodes fragiles et dangereuses.

+0

J'aurais dû le mentionner, je fais du TDD, ce qui m'empêche de tester des cas fictifs: P C'est une méthode de classe publique, mais interne à un module. Donc, lancer une exception ne fait pas partie de l'exigence, donc pas de test. –

+0

Aussi Debug.Fail ne sera toujours pas couvert par le code, en remplaçant également ce lancer avec Debug.Fail est une erreur de compilation. –

+0

@ user93422: J'ai mis à jour ma réponse pour répondre à vos questions. –

4

Je ne comprends pas pourquoi vous ne voudriez pas écrire un test unitaire qui utilise un "code qui ne devrait pas arriver".

Si cela "ne devrait pas arriver", alors pourquoi écrivez-vous le code? Parce que vous pensez que cela pourrait arriver bien sûr - peut-être qu'une erreur dans un futur refactoring va casser votre autre validation et que ce code sera exécuté.

Si vous pensez que cela vaut la peine d'écrire le code, il vaut la peine de le tester.

+0

Je suis d'accord - écrire un code qui «ne devrait jamais être exécuté» me semble être un exercice plutôt inutile. – EnOpenUK

+0

bon point. J'écris ce code parce que C# requiert une méthode non void pour finir soit avec return ou throw. Je préférerais préférer l'exception de lancement au moment de l'exécution. Donc je n'ai pas besoin d'écrire cette ligne moche. Ce qui signifie que j'ai besoin d'une solution de contournement pour la fonctionnalité de langue, ou trouver une faille dans la conception. –

+1

Pour améliorer la conception, j'utiliserais une méthode plutôt qu'une propriété. Une propriété "ressemble" à une simple variable membre accès à l'appelant, donc ils sont susceptibles d'être * surpris * si cela prend beaucoup de temps à exécuter, a des effets secondaires (comme changer les variables membres dans un appel 'get'), provoque événements à déclencher ou déclenche une exception. D'un autre côté, il est normal qu'une méthode renvoie une valeur pour indiquer "non trouvé" (par exemple -1, comme string.IndexOf) ou une exception - une méthode est donc un meilleur choix de conception. –

3

Une partie de l'objectif de l'essai est de tester les deux choses qui devrait arriver et les choses qui peuvent arriver.

Si vous avez du code qui devrait jamais exécuter, mais pourrait dans les bonnes conditions (ou mal), vous pouvez vérifier que l'exception se produit dans les bonnes conditions (dans Unit Test Framework de MS) en décorant votre test méthode avec:

[ExpectedException(typeof(InvalidOperationException))] 
+0

Je fais du TDD, en tant que tel, je ne teste pas les choses qui peuvent _can_ mais jamais _should_ arriver. Dans ce cas, cette exception est une indication d'un bogue dans le code, et PAS une condition exceptionnelle. –

+0

Une façon de répéter ce que vous avez écrit est qu'il y a des choses que vous savez qui peuvent arriver mais que vous ne faites rien! Puisque je ne pense pas que vous souhaitiez laisser votre programme ouvert à un comportement indéfini, vous pourriez vouloir réfléchir davantage à votre stratégie de test et de codage! Autant que possible, vos exigences devraient être de traiter de manière satisfaisante tous les intrants possibles, y compris ceux qui sont indésirables. Écrire des tests pour vérifier que votre programme répond à ces exigences. –

4

Il est parfois nécessaire d'écrire des petits morceaux de code qui ne peut pas exécuter, juste pour apaiser un compilateur (par exemple, si une fonction qui jettent toujours une exception, quitte l'application, etc. est appelé à partir une fonction qui est supposée renvoyer une valeur, le compilateur peut insister pour que l'appelant inclue un "return 0", "Return Nothing" ", etc. déclaration qui ne peut jamais être exécutée. Je ne pense pas que l'on devrait être obligé de tester un tel "code", car il peut être impossible de le faire exécuter sans sérieusement casser d'autres parties du système.

Une question plus intéressante vient avec le code que le processeur ne devrait jamais être capable d'exécuter dans des circonstances normales, mais qui existe pour minimiser les dommages causés par des circonstances anormales. Par exemple, certaines applications critiques pour la sécurité que j'ai début écrit avec quelque chose comme code (application réelle est le code de la machine, étant donné ci-dessous est pseudo-code)

 
    register = 0 
    test register for zero 
    if non-zero goto dead 
    register = register - 1 
    test register for zero 
    if non-zero goto okay1 
dead: 
    shut everything down 
    goto dead 
okay1: 
    register = register + 1 
    if non-zero goto dead 

Je ne sais pas exactement comment on pourrait s'y prendre pour les tests ce code dans le cas d'échec. Le but du code est de s'assurer que si le registre a subi des dommages électrostatiques ou autres, ou qu'il ne fonctionne pas, le système s'arrêtera en toute sécurité. En l'absence de matériel très sophistiqué, je ne sais pas comment tester un tel code.

Questions connexes