33 votes

Debug.Assert vs. les exceptions lancées spécifiques

Je viens de commencer à parcourir 'Debugging MS .Net 2.0 Applications' de John Robbins, et j'ai été troublé par son évangélisation de Debug.Assert(...).

Il fait remarquer que les alertes bien implémentées stockent l'état, en quelque sorte, d'une condition d'erreur, par exemple :

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter");

Personnellement, je trouve fou qu'il aime tant répéter son test sans un commentaire de "logique commerciale" sensé, peut-être "i <= 3 ne doit jamais se produire à cause du processus de widgitification de flobittyjam".

Donc, je pense que je comprends les assertions comme une sorte de bas niveau "protégeons mes hypothèses"... en supposant que l'on pense que c'est un test que l'on doit seulement faire en débogage - c'est-à-dire que vous vous protégez contre vos collègues et futurs programmeurs, et que vous espérez qu'ils testent réellement les choses.

Mais ce que je ne comprends pas, c'est qu'il dit ensuite que vous devriez utiliser des assertions en plus de la gestion normale des erreurs ; ce que j'envisage, c'est quelque chose comme ceci :

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status");
if (i <= 3)
{
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString());
}

Qu'est-ce que j'ai gagné en faisant répéter par Debug.Assert le test de la condition d'erreur ? Je pense que je comprendrais si nous parlions de la double vérification en débogage seulement d'un calcul très important...

double interestAmount = loan.GetInterest();
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount, "Mismatch on interest calc");

...mais je ne l'obtiens pas pour les tests de paramètres qui valent sûrement la peine d'être vérifiés (dans les versions DEBUG et Release)... ou pas. Qu'est-ce qui me manque ?

48voto

Chris Jester-Young Points 102876

Les assertions ne servent pas à vérifier les paramètres. La vérification des paramètres doit toujours être effectuée (et précisément en fonction des conditions préalables spécifiées dans votre documentation et/ou spécification), et l'assertion ArgumentOutOfRangeException jeté si nécessaire.

Les assertions servent à tester les situations " impossibles ", c'est-à-dire les choses que vous (dans la logique de votre programme) supposez sont vraies. Les assertions sont là pour vous dire si ces hypothèses sont brisées pour une raison quelconque.

J'espère que cela vous aidera !

17voto

hwiechers Points 4717

Il y a un aspect de communication entre les assertions et la levée d'exception.

Disons que nous avons une classe Utilisateur avec une propriété Nom et une méthode ToString.

Si ToString est implémenté comme ceci :

public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}

Il est dit que le nom ne doit jamais être nul et qu'il y a un bogue dans la classe User si c'est le cas.

Si ToString est implémenté comme ceci :

public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

Il indique que l'appelant utilise ToString de manière incorrecte si Name est nul et qu'il doit le vérifier avant d'appeler.

La mise en œuvre avec les deux

public string ToString()
{
     Debug.Assert(Name != null);
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

dit que si Name est nul il y a un bug dans la classe User, mais nous voulons le gérer quand même. (L'utilisateur n'a pas besoin de vérifier Name avant d'appeler.) Je pense que c'est le genre de sécurité que Robbins recommandait.

5voto

Mark Simpson Points 10789

J'y ai longuement réfléchi lorsqu'il s'est agi de donner des conseils sur le choix entre déboguer et affirmer en ce qui concerne les tests.

Vous devez être en mesure de tester votre classe avec une entrée erronée, un mauvais état, un ordre d'opérations invalide et toute autre condition d'erreur imaginable. jamais trip. Chaque affirmation vérifie que quelque chose doit toujours être vrai quelles que soient les entrées ou les calculs effectués.

Je suis arrivé à de bonnes règles de base :

  1. Les alertes ne remplacent pas un code robuste qui fonctionne correctement indépendamment de la configuration. Elles sont complémentaires.

  2. Les alertes ne doivent jamais être déclenchées pendant l'exécution d'un test unitaire, même lorsqu'on introduit des valeurs invalides ou qu'on teste des conditions d'erreur. Le code doit gérer ces conditions sans qu'une assertion ne se produise.

  3. Si une assert se déclenche (soit dans un test unitaire, soit pendant les tests), la classe est boguée.

Pour toutes les autres erreurs -- typiquement dues à l'environnement (connexion réseau perdue) ou à une mauvaise utilisation (l'appelant a passé une valeur nulle) -- il est beaucoup plus agréable et plus compréhensible d'utiliser des contrôles et des exceptions. Si une exception se produit, l'appelant sait que c'est probablement sa faute. Si une assert se produit, l'appelant sait que c'est probablement un bug dans le code où se trouve l'assert.

En ce qui concerne la duplication : Je suis d'accord. Je ne vois pas pourquoi vous répliqueriez la validation avec un Debug.Assert ET un contrôle d'exception. Non seulement cela ajoute du bruit au code et brouille les pistes pour savoir qui est en faute, mais c'est une forme de répétition.

4voto

Simon Johnson Points 4641

J'utilise des contrôles explicites qui lèvent des exceptions sur public y protégé et des assertions sur les méthodes privées.

Habituellement, les vérifications explicites empêchent les méthodes privées de voir des valeurs incorrectes de toute façon. Donc, en réalité, l'assert vérifie une condition qui devrait être impossible. Si une assert se déclenche, cela m'indique qu'il y a un défaut dans la logique de validation contenue dans l'une des routines publiques de la classe.

3voto

Steve Steiner Points 4044

Une exception peut être attrapée et avalée, rendant l'erreur invisible pour les tests. Cela ne peut pas se produire avec Debug.Assert.

Personne ne devrait jamais avoir un gestionnaire de capture qui attrape toutes les exceptions, mais les gens le font quand même, et c'est parfois inévitable. Si votre code est invoqué depuis COM, la couche d'interopérabilité attrape toutes les exceptions et les transforme en codes d'erreur COM, ce qui signifie que vous ne verrez pas vos exceptions non gérées. Les alertes ne souffrent pas de ce problème.

De même, lorsque l'exception n'est pas gérée, une meilleure pratique consiste à effectuer un mini-dump. Un domaine dans lequel VB est plus puissant que C# est que vous pouvez utiliser un filtre d'exception pour déclencher un mini-dump lorsque l'exception est en vol, et laisser le reste du traitement des exceptions inchangé. L'article du blog de Gregg Miskelly sur le filtre d'exception injecter fournit un moyen utile de le faire à partir de C#.

Une autre remarque sur les actifs ... ils interagissent mal avec les tests unitaires des conditions d'erreur dans votre code. Il est utile d'avoir un wrapper pour désactiver l'assert pour vos tests unitaires.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X