Partager via


Cet article a fait l'objet d'une traduction automatique.

Série de tests

Test d'injection d'erreur avec TestApi

James McCaffrey

Télécharger l'exemple de code

James McCaffreyPannes d'injection de test consiste à insérer délibérément une erreur dans une application en cours de test, puis en exécutant l'application pour déterminer si l'application traite correctement avec l'erreur. Test de panne d'injection peut prendre plusieurs formulaires différents. Dans l'article de mois-ci ce, j'explique comment introduire des erreurs dans les applications .NET en cours d'exécution à l'aide d'un composant de la bibliothèque TestApi.

La meilleure façon pour voir où je suis se transforme en dans cette colonne est de jeter un œil à la capture d'écran dans les de la figure 1 . La capture d'écran indique que je suis exécute injection d'erreur de test sur une application .NET WinForm fictive nommée TwoCardPokerGame.exe. Un programme c# nommé FaultHarness.exe est en cours d'exécution dans le shell de commande. Il modifie le comportement normal de l'application testée afin que l'application lève une heure d'exception le troisième qu'un utilisateur clique sur le bouton intitulé Evaluate. Dans ce cas, l'application de deux cartes Poker ne gère pas correctement l'exception d'application et le résultat est la boîte de message généré par le système.

Figure 1  Fault Injection Testing in Action

Figure 1 injection d'erreur de test en action

Prendre Let’s approfondie de ce scénario à prendre en compte certains détails impliqués. Lors d'une FaultHarness.exe est lancé à partir de l'invite de commandes, dans les coulisses, l'atelier prépare profilage de code qui intercepte l'exécution de code normal de TwoCard ­ PokerGame.exe. Il s'agit de la session d'injection de pannes.

La session de d'injection d'erreur utilise une DLL pour démarrer la surveillance pour les appels de méthode de l'application Button2_Click, qui est le Gestionnaire d'événements pour le bouton intitulé Evaluate. La session de d'injection d'erreur a été configurée pour que le premier utilisateur double clique sur le bouton d'Evaluate, l'application se comporte comme codée, mais le troisième clic la session de l'erreur, l'application lever une exception de type System.ApplicationException.

La session de l'erreur enregistre l'activité de la session et un ensemble de fichiers ouvre une session sur l'ordinateur hôte de test. Notez que dans la figure 1 de que l'application de deux premières transactions-évaluation cliquez sur travail paires correctement, mais la, cliquez sur tiers a généré une exception.

Dans les sections qui suivent, j'allez brièvement décrire la factice deux carte jeu de poker application tester, présenter et expliquez en détail le code dans le programme FaultHarness.exe illustré à la de la figure 1 et fournit quelques conseils relatifs à lorsque l'utilisation des tests d'injection de pannes est appropriée et lorsque les autres techniques sont plus adaptées. Bien que le programme FaultHarness.exe lui-même est très simple et la plupart des tâches difficiles est effectuée en arrière-plan par les DLL TestApi, comprendre et de modifier le code que je présente ici pour répondre à vos propres tests de scénarios le nécessite une bonne compréhension de l'environnement de programmation .NET. Cela dit, même si vous êtes un débutant de .NET, vous devez pouvoir suivre mes explications sans trop de difficultés. Je suis certain de que vous y trouverez un complément intéressant et utile éventuellement la discussion d'injection d'erreur pour votre ensemble d'outils.

L'application testée

Mon application factice en cours de test est une application WinForm c# simpliste mais représentative qui simule un jeu de cartes hypothétique appelé deux carte poker. L'application se compose de deux composants principaux : TwoCardPokerGame.exe fournit l'interface utilisateur et le TwoCardPokerLib.dll fournit la fonctionnalité sous-jacente.

Pour créer la DLL jeu I lancement de Visual Studio 2008 et sélectionné le modèle de bibliothèque de classes c# à partir du fichier | boîte de dialogue Nouveau projet. J'ai nommé la bibliothèque TwoCardPokerLib. La structure générale de la bibliothèque d'est présentée dans de la figure 2 . Le code de TwoCardPokerLib est trop long pour le présenter dans son intégralité dans cet article. Le code source complet pour la bibliothèque TwoCardPokerLib et l'atelier d'injection de pannes FaultHarness est disponible dans le téléchargement de code qui accompagne cet article.

La figure 2 de La bibliothèque TwoCardPokerLib

using System;
namespace TwoCardPokerLib {
  // -------------------------------------------------
  public class Card {
    private string rank;
    private string suit;
    public Card() {
      this.rank = "A"; // A, 2, 3, . . ,9, T, J, Q, K
      this.suit = "c"; // c, d, h, s
    }
    public Card(string c) { . . . }
    public Card(int c) { . . . }
    public override string ToString(){ . . . }
    public string Rank { . . . }
    public string Suit { . . . }
    public static bool Beats(Card c1, Card c2) { . . . }
    public static bool Ties(Card c1, Card c2) { . . . }
  } // class Card

  // -------------------------------------------------
  public class Deck {
    private Card[] cards;
    private int top;
    private Random random = null;

    public Deck() {
      this.cards = new Card[52];
      for (int i = 0; i < 52; ++i)
        this.cards[i] = new Card(i);
      this.top = 0;
      random = new Random(0);
    }

    public void Shuffle(){ . . . }
    public int Count(){ . . . } 
    public override string ToString(){ . . . }
    public Card[] Deal(int n) { . . . }
    
  } // Deck

  // -------------------------------------------------
  public class Hand {
    private Card card1; // high card
    private Card card2; // low card
    public Hand(){ . . . }
    public Hand(Card c1, Card c2) { . . . }
    public Hand(string s1, string s2) { . . . }
    public override string ToString(){ . . . }
    private bool IsPair() { . . . }
    private bool IsFlush() { . . . }
    private bool IsStraight() { . . . }
    private bool IsStraightFlush(){ . . . }
    private bool Beats(Hand h) { . . . }
    private bool Ties(Hand h) { . . . }
    public int Compare(Hand h) { . . . }
    public enum HandType { . . . }
    
 } // class Hand

} // ns TwoCardPokerLib

Code d'interface utilisateur de l'application

Une fois que j'ai eu le code de bibliothèque sous-jacent TwoCardPokerLib terminé, j'ai créé un composant d'interface utilisateur factice. J'ai démarré un nouveau projet dans Visual Studio 2008, à l'aide du modèle d'application de Windows Form Visual c#, j'ai nommé mon application TwoCardPokerGame.

À l'aide du concepteur Visual Studio, j'ai fait glisser un contrôle Label de la collection de boîte à outils jusqu'à l'aire de conception d'application et modifiés propriété Text du contrôle de “ textBox1 ” pour “ deux carte poker. ” Ensuite, j'ai ajouté des deux autres contrôles Label (“ votre main ” et “ de travail de main ”), deux contrôles TextBox, deux contrôles Button (“ offre ” et “ Evaluate ”) et un contrôle ListBox. Je n'a pas changé les noms de contrôle par défaut d'une des huit contrôles — textBox1, textBox2, button1 et donc sur.

Une fois que ma conception était en place, j'ai double-cliqué sur le contrôle button1 pour demander à Visual Studio génère un squelette de gestionnaire d'événements pour le bouton et de charger le fichier Form1.cs dans l'éditeur de code. À ce stade, j'ai lié sur le projet TwoCardPokerGame dans la fenêtre Explorateur de solutions, sélectionné l'option Ajouter une référence dans le menu contextuel et vers lequel pointe le fichier TwoCardPokerLib.dll. Dans Form1.cs, j'ai ajouté un à l'aide de relevé afin que je n'avez pas besoin de qualifier entièrement les noms dans la bibliothèque de classes.

Ensuite, j'ai ajouté quatre objets de portée de la classe statique pour mon application :

namespace TwoCardPokerGame {
  public partial class Form1 : Form {
    static Deck deck;
    static Hand h1;
    static Hand h2;
    static int dealNumber; 
...

Objet h1 est le stock pour l'utilisateur et h2 est le stock pour l'ordinateur. Ensuite, j'ai ajouté du code d'initialisation pour le constructeur du formulaire :

public Form1() {
  InitializeComponent();
  deck = new Deck();
  deck.Shuffle();
  dealNumber = 0;
}

Le constructeur de document crée un jeu de 52 cartes, afin du ROI de pique et la réorganisation de la méthode à partir de l'as de trèfle
rend aléatoire l'ordre des cartes dans le pont.

Ensuite, j'ai ajouté la logique du code à la méthode Button1_Click, comme dans de la figure 3 . Pour chacune des deux mains, j'appelle la méthode Deck.Deal pour supprimer deux cartes de l'objet de pont. Puis je transmettre ces deux cartes au constructeur de la main et afficher la valeur de la main dans un contrôle TextBox. Notez que la méthode Button1_Click gère toute exception en affichant un message dans le contrôle ListBox.

La figure 3 Gestion des cartes des

private void button1_Click(
  object sender, EventArgs e) { 

  try  {
    ++dealNumber;
    listBox1.Items.Add("Deal # " + dealNumber);
    Card[] firstPairOfCards = deck.Deal(2);
    h1 = new Hand(firstPairOfCards[0], firstPairOfCards[1]);
    textBox1.Text = h1.ToString();

    Card[] secondPairOfCards = deck.Deal(2);
    h2 = new Hand(secondPairOfCards[0], secondPairOfCards[1]);
    textBox2.Text = h2.ToString();
    listBox1.Items.Add(textBox1.Text + " : " + textBox2.Text);
  }
  catch (Exception ex) {
    listBox1.Items.Add(ex.Message);
  }
}

Ensuite, dans la fenêtre du concepteur Visual Studio j'ai double-cliqué sur le contrôle button2 pour générer automatiquement le Gestionnaire d'événements du contrôle
squelette. J'ai ajouté du code simple pour comparer deux objets de la main et d'afficher un message dans le contrôle ListBox. Notez que la méthode Button2_Click ne gère pas directement toutes les exceptions :

private void button2_Click(
  object sender, EventArgs e) {
  int compResult = h1.Compare(h2);
  if (compResult == -1)
    listBox1.Items.Add(" You lose");
  else if (compResult == +1)
    listBox1.Items.Add(" You win");
  else if (compResult == 0)
    listBox1.Items.Add(" You tie");

  listBox1.Items.Add("-------------------------");
}

L'atelier de injection d'erreur

Avant de créer le faisceau de d'injection de pannes, illustré à la de la figure 1 , j'ai téléchargé la clé de DLL sur mon ordinateur hôte de test. Ces DLL font partie d'une collection de bibliothèques .NET nommé TestApi et peut être trouvé à testapi.codeplex.com de .

La bibliothèque TestApi est un ensemble d'utilitaires logiciels-test-liés. Inclus dans la bibliothèque TestApi est un ensemble d'API d'injection Managed Code panne. (En savoir plus sur les à blogs.msdn.com/b/ivo_manolov/archive/2009/11/25/9928447.aspx de .) J'ai téléchargé l'injection de panne plus récente version de l'API, qui, dans mon cas, était la version 0,4 et décompressé le téléchargement. J'expliquerai ce qui se trouve dans le téléchargement et l'emplacement où placer les fichiers binaires d'injection de pannes sous peu.

Version 0.4 prend en charge l'injection d'erreur de test pour les applications créées à l'aide du .NET Framework 3.5. La bibliothèque TestApi est en cours de développement actif, afin que vous consultez le site de CodePlex pour les mises à jour pour les techniques que je présente dans cet article. En outre, vous souhaitez rechercher les mises à jour et des conseils sur le blog de Bill Liu, le développeur principal de la bibliothèque de d'injection d'erreur TestApi, à blogs.msdn.com/b/billliu/ de .

Pour créer le faisceau de d'injection d'erreur, j'ai démarré un nouveau projet dans Visual Studio 2008 et sélectionné le modèle Application Console c#. J'ai nommé à l'application FaultHarness et j'ai ajouté du code minimal pour le modèle de programme (voir la rubrique de la figure 4 ).

La figure 4 FaultHarness

using System;
namespace FaultHarness {
  class Program {
    static void Main(string[] args) {
      try {
        Console.WriteLine("\nBegin TestApi Fault Injection environmnent session\n");

        // create fault session, launch application

        Console.WriteLine("\nEnd TestApi Fault Injection environment session");
      }
      catch (Exception ex) {
        Console.WriteLine("Fatal: " + ex.Message);
      }
    }
  } // class Program
} // ns

J'ai d'accès de la clé <F5> pour générer et exécuter le squelette de harnais qui a créé un dossier \bin\debug dans le dossier de racine FaultHarness.

Le téléchargement TestApi possède deux composants clés. La première est TestApiCore.dll, ce qui se trouve dans le dossier Binaries du téléchargement décompressé. J'ai copié cette DLL dans le répertoire racine de l'application FaultHarness. Puis j'ai lié sur le projet FaultHarness dans la fenêtre Explorateur de solutions, sélectionné d'ajouter une référence et pointé à TestApiCore.dll. Ensuite, j'ai ajouté un à l'aide de l'instruction pour Microsoft.Test.FaultInjection vers le haut de mon code faisceau de panne pour que mon atelier code peut accéder directement à la fonctionnalité dans TestApiCore.dll. J'ai également ajouté un à l'aide de l'instruction pour System.Diagnostics car, comme vous le verrez bientôt, je souhaite accéder aux classes Process et ProcessStartInfo à partir de cet espace de noms.

Le second composant clé dans le téléchargement d'injection de pannes est un dossier nommé FaultInjectionEngine. Il conserve des versions 32 bits et 64 bits de FaultInjectionEngine.dll. J'ai copié le dossier de InjectionEngine tout faute ­ dans le dossier maintenant mon FaultHarness exécutable dans mon cas C:\FaultInjection\FaultHarness\bin\Debug\. La version du système d'injection d'erreur j'utilisais 0,4 requiert le dossier FaultInjectionEngine pour être le même emplacement que l'atelier exécutable. En outre, le système nécessite que l'application dans les binaires de test se trouver dans le même dossier que le fichier exécutable de l'atelier de sorte que j'ai copié les fichiers TwoCardPokerGame.exe et TwoCard ­ PokerLib.dll dans C:\FaultInjection\FaultHarness\bin\Debug\.

Pour résumer, lorsque vous utilisez le système d'injection d'erreur TestApi, une bonne approche consiste à générer un faisceau squelette et l'exécuter afin qu'un répertoire \bin\debug de faisceau est créé, puis placez le fichier TestApiCore.dll dans le répertoire racine de faisceau, placez le dossier FaultInjectionEngine dans \bin\debug et placent l'application sous les binaires de test (.exe et .dll) en \bin\debug également.

En utilisant le système d'injection d'erreur TestApi requiert que vous spécifiez l'application testée, la méthode de l'application en cours de test qui déclenchera une erreur de la condition qui déclenche une erreur et le type d'erreur qui sera déclenché :

string appUnderTest = "TwoCardPokerGame.exe";
string method = 
  "TwoCardPokerGame.Form1.button2_Click(object, System.EventArgs)";
ICondition condition =
  BuiltInConditions.TriggerEveryOnNthCall(3);
IFault fault =
  BuiltInFaults.ThrowExceptionFault(
    new ApplicationException(
    "Application exception thrown by Fault Harness!"));
FaultRule rule = new FaultRule(method, condition, fault);

Notez que, étant donné que le système nécessite l'application testée se trouver dans le même dossier que l'exécutable de l'atelier, le nom de l'application dans l'exécutable de test n'a pas besoin le chemin d'accès à son emplacement.

Spécifier le nom de la méthode qui déclenche l'erreur injecté est une source courante de problèmes pour les débutants de d'injection d'erreur TestApi. Le nom de méthode doit être entièrement qualifié dans le space.Class.Method(args) de ­ nom de formulaire. Ma méthode recommandée consiste à utiliser l'outil ildasm.exe pour examiner l'application en cours de test pour vous aider à déterminer la signature de la méthode de déclenchement. À partir de Visual Studio spécial outils commande shell, je lance ildasm.exe, pointez sur l'application testée, puis cliquez deux fois sur la méthode cible. La figure 5 présente un exemple d'utiliser ildasm.exe pour examiner la signature de la méthode Button2_Click.

Figure 5 Using ILDASM to Examine Method Signatures

La figure 5 utilisation d'ILDASM pour examiner les signatures de méthode

Lorsque vous spécifiez la signature de méthode de déclencheur, vous n'utilisez pas le type de retour de méthode et que vous n'utilisez pas les noms de paramètres. Obtention de la signature de méthode correcte parfois nécessite un peu de tâtonnement. Par exemple, lors de ma première tentative pour cibler Button2_Click, j'ai utilisé :

TwoCardPokerGame.Form1.button2_Click(object,EventArgs)

J'ai dû pour la corriger :

TwoCardPokerGame.Form1.button2_Click(object,System.EventArgs)

Le téléchargement de TestApi contient un dossier de documentation qui contient un document de concepts qui fournit de bons conseils sur la façon de créer correctement des différents types de signatures de méthode, y compris des constructeurs, les méthodes génériques, les propriétés et les opérateurs surchargés. Ici je cibler une méthode qui se trouve dans l'application testée, mais je pourrais ont également cible une méthode dans la de deux sous-jacent ­ CardPokerLib.dll, telles que :

string method = "TwoCardPokerLib.Deck.Deal(int)"

Après avoir spécifié la méthode de déclenchement, l'étape suivante consiste à spécifier la condition sous laquelle l'erreur est injectée dans l'application testée. Dans mon exemple j'ai utilisé TriggerEveryOnNthCall(3) que comme vous l'avez vu injecte un défaut de chaque troisième fois, le déclencheur de la méthode est appelée. Le système d'injection d'erreur TestApi possède un ensemble claire des conditions du déclencheur dont TriggerIfCalledBy(method), TriggerOnEveryCall et d'autres personnes.

Après avoir spécifié la condition du déclencheur, l'étape suivante consiste à spécifier le type de panne qui va être injecté dans le système testé. J'ai utilisé BuiltInFaults.ThrowExceptionFault. Plus les erreurs d'exception, le système d'injection d'erreur TestApi possède les erreurs de type de retour prédéfinis qui vous permettent d'injecter des valeurs de retour erronés dans votre application en cours de test en cours d'exécution. Par exemple, cela provoquera la méthode trigger retourner une valeur de -1 (sans doute incorrecte) :

IFault f = BuiltInFaults.ReturnValueFault(-1)

Après la méthode de déclenchement de pannes, condition et type de défaillance ont été spécifié, l'étape suivante consiste à créer un nouveau FaultRule et transmettre cette règle à une nouveau FaultSession :

FaultRule rule = new FaultRule(method, condition, fault);
Console.WriteLine(
  "Application under test = " + appUnderTest);
Console.WriteLine(
  "Method to trigger injected runtime fault = " + method);
Console.WriteLine(
  "Condition which will trigger fault = On 3rd call");
Console.WriteLine(
  "Fault which will be triggered = ApplicationException");
FaultSession session = new FaultSession(rule);

Avec tous les prérequis en place, la dernière partie de l'écriture de code panne que faisceau est à lancer par programme l'application testée dans l'environnement de session de pannes :

ProcessStartInfo psi = 
  session.GetProcessStartInfo(appUnderTest);
Console.WriteLine(
  "\nProgrammatically launching application under test");
Process p = Process.Start(psi);
p.WaitForExit();
p.Close();

Lorsque vous exécutez le faisceau de pannes, il lance l'application en cours de test dans votre session de pannes, avec le ­ de FaultInjection Engine.dll regardent pour les situations où la méthode de déclenchement est appelée lorsque la condition de déclencheur a la valeur true. Les tests sont effectués manuellement ici, mais vous pouvez également exécuter l'automatisation des tests dans une session de l'erreur.

Pendant l'exécution de la session de pannes, les informations sur la session sont enregistrées dans le répertoire en cours, autrement dit, le répertoire qui contient le fichier exécutable faisceau de pannes et l'application dans l'exécutable de test. Vous pouvez examiner ces fichiers journaux pour aider à résoudre les problèmes qui peuvent se produire alors que vous développez votre faisceau d'injection de pannes.

Discussion

L'exemple et des explications, que j'ai présenté ici doivent vous aider en cours d'exécution avec la création d'un atelier de d'injection de pannes pour votre propre application en cours de test. Comme avec toute activité qui fait partie du processus de développement de logiciels, ressources se sont limitées et vous devez analyser les coûts et avantages de l'exécution de tests d'injection de pannes. En cas de certaines applications, l'effort requis pour créer injection d'erreur de test ne peut être utile, mais il n'y a de nombreux scénarios de test où la défaillance d'injection de test est extrêmement importante. Imaginez le logiciel qui contrôle un équipement médical ou un système de vol. Dans des situations comme celles-ci, les applications doivent absolument être robuste et capable de gérer correctement tous les types de défaillances inattendues.

Un certain ironie est liées à la défaillance d'injection de test. L'idée est que, si vous pouvez anticiper les situations lorsqu'une exception peut se produire, vous pouvez en théorie souvent par programme se protéger contre cette exception et tester le comportement correct de ce comportement de protection. Toutefois, même dans ce cas, le test de panne d'injection est utile pour générer difficiles à créer des exceptions. En outre, il est possible d'injecter des erreurs qui sont très difficiles à anticiper, telles que l'exception System.OutOfMemoryException.

Erreur d'injection de test est associé à et parfois confondu avec les tests de mutation. Tester la mutation, insérez délibérément les erreurs dans le système en cours de test, mais une série de tests existante par rapport au système défaillant puis l'exécuter afin de déterminer si la série de tests intercepte les erreurs de nouveau créés. Test de la mutation est un moyen de mesurer l'efficacité de test suite et, au final, augmenter la couverture de scénarios de test. Comme vous l'avez vu dans cet article, le principal objectif des tests d'injection de panne est de déterminer si le système testé gère correctement les erreurs.

Dr. James McCaffrey travaille pour Volt Information Sciences Inc., où il gère la formation technique d'ingénieurs logiciels travaillant sur le Microsoft Redmond, Washington, campus. Il a travaillé sur plusieurs produits Microsoft, y compris Internet Explorer et MSN Search. Dr. McCaffrey est l'auteur de “ .NET Test Automation Recipes ” (Apress, 2006) et peut être contacté à jammc@microsoft.com de.

Merci aux experts techniques suivantes pour la révision de cet article : Bill Liu et Paul Newson