• 10 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 10/12/2018

Fixez enfin ce bug !

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Nous en sommes toujours à rechercher la source du bug qui cause l’apparition d’une ArgumentNullException lors de l’exécution du test unitaire VerifyAddTask dans le fichier T_TodoModel.cs. Il est temps de conclure cette chasse au bug !

Trouvez la cause du  bug

Avec une gestion des exceptions et des points d’arrêts, nous en sommes arrivés au code de la méthode InternalAddTask et tentons de déterminer d’où peut venir l’exception :

Implémentation de InternalAddTask
Implémentation de InternalAddTask

Au vu de ce code, il va falloir descendre dans l'implémentation de InternalAddTask avec Step Into (F11). Si le test de la première ligne échoue, une InvalidOperationException sera levée mais ce n'est pas l'ArgumentNullException qui est détecté.

Il est donc possible que l'exception soit levée dans le test lui-même lors de l'insertion dans le champ _tasks (qui est une instance de List<TodoTask>).

Comment lever le doute sans exécuter ou descendre dans GetTask ?

On peut déjà vérifier que ni la task reçue, ni le nom passé en paramètre, sont "null" :

Les paramètres de GetTask ne sont pas
Les paramètres de GetTask ne sont pas "null"

En revanche, en passant la souris sur le champ _tasks, on dirait bien que sa valeur est "null" :

_task est null
_task est null

Partons donc de cette hypothèse et descendons dans la méthode GetTask avec Step Into (F11) :

_task est vraiment null
_task est vraiment null

L'éditeur indique que _tasks vaut "null" comme nous avions pu le constater dans la méthode appelante. Il est donc maintenant évident que l'appel à FirstOrDefault va échouer parce que la liste sur laquelle cette méthode va opérer sera "null" : nous avons maintenant la raison pour laquelle l'ArgumentNullException va être levée ! ;) 

Fixez le problème

Au vu de l'implémentation de la classe TodoModel (pas de constructeur, ni d’initialisation explicite du champs _tasks), on dirait bien que le champ _tasks n'a pas été initialisé. Il suffit donc d'ajouter le code suivant :

Pensez à initialiser _task
Pensez à initialiser _task

Ce n’est pas terminé !

Rappelez-vous : il faut maintenant s'assurer que le fix résout bien le problème initial. Pour cela, il suffit de relancer le test unitaire et de vérifier qu'il passe au vert. Malheureusement, ce n'est pas le cas !

VerifyAddTest échoue encore
VerifyAddTest échoue encore

Cette fois-ci, nous tombons sur une exception, mais de type InvalidOperationException. Le message indique qu'il n'est pas possible d'insérer la tâche "sleep" une seconde fois.

Rappelez vous qu'un bug n'est vraiment fixé que lorsque tous les tests sont passés au vert après une correction. Comme ce n'est pas le cas ici, eh bien il faut poursuivre notre enquête...

Démarrez une nouvelle investigation

Comment démarrer cette nouvelle investigation ?

Une première façon est de regarder le code depuis lequel l'exception est levée, et de mettre un point d'arrêt, non pas sur la ligne, mais sur le throw avec un clic-droit ou le raccourci clavier F9 :

posez un breakpoint sur le throw
posez un breakpoint sur le throw

Le débogueur arrêtera donc l'exécution juste avant que l'exception ne soit levée.

Si vous vous souvenez du chapitre précédent, vous pourriez aussi avoir envie de mettre un point d’arrêt sur l’appel à CreateTask dans la boucle foreach :

Définissez une condition sur le point d'arrêt
Définissez une condition sur le point d'arrêt

Vous pouvez aussi définir un point d’arrêt conditionnel de type Hit Count pour mettre en pause avant l’exécution de la quatrième itération (c’est-à-dire avant que la seconde tâche avec le nom "sleep" ne soit ajoutée) :

Définissez une condition sur 4 itérations
Définissez une condition sur 4 itérations

Lorsqu'on relance le test unitaire, on s'aperçoit que l'exception est levée s'il existe une tâche avec le même nom, comme on pouvait s'en douter. Il faut donc décider s'il faut conserver cette fonctionnalité ou modifier le test unitaire, voire en ajouter un autre pour "officialiser" le comportement attendu.

Résumé

Comme vous pouvez le constater, une investigation peut nécessiter plusieurs itérations de recherche de bug. Surtout, gardez à l'esprit que plusieurs issues sont possibles la plupart du temps. Je vous conseille cependant de privilégier l’ajout de tests unitaires qui rendent les comportements des composants testés beaucoup plus explicites !

La partie suivante de ce cours sur le débogage d’applications C# vous donnera de nouveaux moyens de visualiser et même de modifier les données manipulées par le code. Cela vous rendra plus efficace et vous aidera à mieux comprendre le code que vous serez amenés à maintenir… sans que vous l’ayez écrit. :p

Exemple de certificat de réussite
Exemple de certificat de réussite