Servez-vous du panneau Test Explorer
Pour le scénario d'un test qui échoue à cause d'une exception inattendue, le panneau de Test Explorer est votre point de départ. Pour y accéder, allez dans le menu TEST | Windows | Test Explorer :
Dans notre cas, sélectionnez le test VerifyAddTasks et démarrez-le avec un clic-droit Run Selected Tests :
La fenêtre de résultats du panneau Test Explorer de Visual Studio fournit un minimum d'informations pour vous aider à comprendre la cause de l’échec du test :
En premier, l’échec vient d’une exception levée par le code appelé (encadré rouge).
Ensuite, les détails de l’exception en question sont présentés (encadré vert) :
Son type (ici ArgumentNullException) ;
Et message Value cannot be null.
Que vous donne cette information ?
Le type de l’exception ainsi que le message indiquent qu’une méthode a sûrement reçu un paramètre avec une valeur nulle alors que ce n’était pas supporté. Il vous faut donc maintenant déterminer quelle est cette méthode qui lui passe null. Notez que le nom de ce paramètre est donné juste en dessous : source. Il correspond à l’énumération sur laquelle FirstOrDefault est appelée.
Vous pouvez trouver une partie de la réponse sous la section StackTrace : la pile des appels de méthode est affichée du dernier avant l’exception jusqu’au test unitaire (encadré bleu).
Paramétrez la gestion des exceptions
Reprenons notre investigation. Il est malheureusement trop tard pour faire quoi que ce soit.
En effet, il aurait fallu demander au débogueur d’arrêter l’exécution au moment et à l’endroit précis dans le code où l'exception est levée et non pas quand le test a échoué.
Pour cela, il suffit de passer par le menu DEBUG | Windows | Exception Settings :
Le panneau qui apparaît liste tous les types d’exceptions pouvant être levées :
Nous ne sommes intéressés que par celles levées dans le cadre de .NET qui apparaissent sous la section Common Language Runtime Exceptions. Si l’on coche la case en face d’une exception (dans notre cas ArgumentNullException), le débogueur suspendra l’exécution, et l’instruction qui lève l’exception sera montrée dans l'éditeur.
Pour permettre ce comportement, cette fois-ci, le test est démarré à partir de la commande Debug (et non plus Run) :
Cela signifie que le code va s'exécuter sous le contrôle du débogueur de Visual Studio.
Ainsi, le débogueur va s'arrêter sur la ligne de code levant l'exception. Dans notre cas, il s'arrête sur l’appel à la méthode FirstOrDefault :
La ligne en question est surlignée dans l’éditeur de code et un petit encart donne les détails de l'exception, comme son message.
Comme vous ne connaissez pas le code du test unitaire ni les méthodes qui y sont appelées, il est clairement très difficile de comprendre ce qui se passe et où en est l'exécution. Heureusement, le panneau Call Stack montrant la pile d'appels s'est automatiquement affiché lors de la détection de l'exception. Il permet de comprendre où nous en sommes de l'exécution à ce moment précis :
La méthode en haut de la pile indiquée avec la flèche incurvée, TodoModel.GetTask, est celle dans laquelle l'exception s'est produite. Elle est apparue automatiquement dans l’éditeur de code de Visual Studio.
La méthode suivante dans la pile, TodoModel.InternalAddTask, correspond à celle l'ayant appelée. On peut ainsi remonter dans le temps jusqu'à savoir à quel endroit du test nous en sommes. Pour cela, il suffit de descendre jusqu'à la méthode de T_TodoModel et de double-cliquer sur la ligne VerifyAddTasks (surlignée en bleu sur la copie d'écran précédente) :
La petite flèche incurvée apparaît à la fois dans la pile d'appels et dans le code de la méthode pour indiquer l'instruction correspondant à l'appel de la méthode TodoModel.CreateTask, c'est-à-dire la ligne au-dessus dans la pile d'appels :
Cette gymnastique permet de retrouver l’enchaînement des appels de méthodes ayant abouti à l’exception.
Si l’on se remet dans notre investigation, la collection _tasks sur laquelle est appelée FirstOrDefault semble ne pas être initialisée. Il serait intéressant de connaître la valeur de taskName pour laquelle l’exception a été levée. Il suffit pour cela de déplacer la souris dessus et sa valeur s’affichera directement dans l’éditeur, "sleep" dans notre cas :
Le dernier chapitre de ce cours reviendra en détail sur les différents moyens de visualiser et de modifier la valeur des paramètres et des champs des structures de données.
Résumé
Nous venons de voir comment demander au débogueur de s’arrêter dès qu’une exception est levée. Nous avons aussi visualisé l’enchaînement des exécutions de méthodes avec le panneau de la pile d’appels.
La question est maintenant de savoir comment demander au débogueur de s’arrêter, non pas au moment où l’exception est levée, mais plutôt à l’endroit du test unitaire qui va mener à l’erreur. On pourra alors retrouver l'état des variables locales de chaque méthode, ainsi que la valeur des différents paramètres utilisés lors des appels consécutifs.
La réponse se trouve dans le chapitre suivant.