Compartir a través de


Este artículo proviene de un motor de traducción automática.

Ejecución de pruebas

Pruebas de inyección de errores con TestApi

James McCaffrey

Descargar el ejemplo de código

James McCaffreyLas pruebas de inyección de errores son el proceso de inserción de deliberadamente un error en una aplicación sometida a prueba y, a continuación, ejecuta la aplicación para determinar si la aplicación trata los errores correctamente. Las pruebas de inyección de errores, pueden tomar formas distintas. En la columna de este mes, explicaré cómo se pueden introducir errores en aplicaciones de .NET en tiempo de ejecución mediante un componente de la biblioteca de TestApi.

La mejor opción para ver mi objetivo en esta columna es examine la captura de pantalla en de figura 1. La captura de pantalla muestra que estoy realizando las pruebas en una aplicación ficticia de WinForm de .NET denominada TwoCardPokerGame.exe de inyección de errores. Un programa de C# denominado FaultHarness.exe se está ejecutando en el shell de comandos. Se modifica el comportamiento normal de la aplicación sometida a prueba, por lo que la aplicación produce un tiempo de excepción de la tercera que un usuario hace clic en el botón evaluar. En este caso, la aplicación de póquer de tarjeta de dos no controla correctamente la excepción de aplicación y el resultado es el cuadro de mensaje generado por el sistema.

Figure 1  Fault Injection Testing in Action

Figura 1 de pruebas en acción de inserción de errores

Let’s, eche un vistazo más en detalle en este caso a considerar algunos de los detalles relacionados. Cuando se inicia FaultHarness.exe desde el shell de comandos, en segundo plano el instrumento de prepara el código de generación de perfiles que interceptará la ejecución de código normal de TwoCard ­ PokerGame.exe. Esto se llama a la sesión de inyección de errores.

La sesión de inyección de errores, utiliza un archivo DLL para iniciar la supervisión para las llamadas al método button2_Click de la aplicación, que es el controlador de eventos para el botón evaluar. Se ha configurado la sesión de inyección de errores para que los primeros dos veces que un usuario hace clic en el botón de evaluar la aplicación se comportan como en el código, pero en el tercero, haga clic en la sesión de error hace que la aplicación se iniciará una excepción de tipo System.ApplicationException.

La sesión de error registra la actividad de sesión y registra un conjunto de archivos para el equipo host de prueba. Observe que en la figura 1 de que la aplicación en primer lugar dos contratos-evaluar haga clic en pares de trabajo correctamente, pero el tercer clic genera una excepción.

En las secciones siguientes, lo almacenaré brevemente el juego de póquer de tarjeta de dos ficticio aplicación sometida a prueba, presentar y explicar en detalle el código del programa de FaultHarness.exe se muestra en de figura 1 se describen y proporcionan algunas sugerencias acerca de cuándo es apropiado el uso de las pruebas de inyección de errores y cuándo son más adecuadas técnicas alternativas. Aunque el propio programa FaultHarness.exe es muy sencillo y los archivos DLL de TestApi realiza la mayor parte del trabajo difícil en segundo plano, (comprender y modificar el código que presento aquí) satisfacer sus propias pruebas escenarios requiere una sólida comprensión del entorno de programación. NET. En dicho, incluso si es un principiante. NET, puede seguir mis explicaciones sin demasiada dificultad. Tengo que encontrará la explicación de inyección de errores de un complemento interesante y útil, posiblemente, para el conjunto de herramientas.

La aplicación sometida a prueba

Mi aplicación ficticia sometida a prueba es una aplicación simple pero representativa de WinForm de C# que simula una baraja hipotético llamado Poker de tarjeta de dos. La aplicación está formada por dos componentes principales: TwoCardPokerGame.exe proporciona la interfaz de usuario y TwoCardPokerLib.dll proporciona la funcionalidad subyacente.

Para crear el archivo DLL de juego inicia Visual Studio 2008 y selecciona la plantilla de biblioteca de clases de C# desde el archivo | cuadro de diálogo nuevo proyecto. Denominé la biblioteca TwoCardPokerLib. La estructura general de la biblioteca se presenta en de figura 2. El código de TwoCardPokerLib es demasiado largo para presentar en su totalidad en este artículo. El código fuente completo para la biblioteca de TwoCardPokerLib y los cables de inyección de errores de FaultHarness está disponible en la descarga de código que acompaña a este artículo.

La figura 2 de La biblioteca 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

El código de interfaz de usuario de aplicación

Una vez que tenía el código subyacente de biblioteca de TwoCardPokerLib terminado, he creado un componente ficticio de la interfaz de usuario. Inició un nuevo proyecto de Visual Studio 2008 mediante la plantilla de aplicación de WinForm de C# y denomina mi aplicación TwoCardPokerGame.

Mediante el Diseñador de Visual Studio, arrastra un control Label de la colección de cuadro de herramientas hasta la superficie de diseño de la aplicación y modifica la propiedad Text de “ textBox1 ” a “ dos tarjetas póquer. ” A continuación, he agregado dos controles Label de más (“ su mano ” y “ equipos de la mano ”), dos controles TextBox, dos controles Button (“ contratos ” y “ Evaluate ”) y un control ListBox. No cambiar los nombres de control predeterminado de cualquiera de los controles de ocho, textBox1, textBox2, button1 y, en.

Cuando el diseño en su lugar, se hizo de doble clic en el control button1 para que Visual Studio para generar un esqueleto del controlador de eventos para el botón y cargar el archivo Form1.cs en el editor de código. En este momento I right-clicked en el proyecto TwoCardPokerGame en la ventana del explorador de soluciones, seleccionado la opción Agregar referencia en el menú contextual y, al que señala al archivo TwoCardPokerLib.dll. En Form1.cs, he agregado el uso de una instrucción para que necesitaría calificar totalmente los nombres de clase en la biblioteca.

A continuación, agregan cuatro objetos static de ámbito de la clase a la aplicación:

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

La mano del usuario es de objeto h1 y h2 la mano para que el equipo. A continuación, agregué un código de inicialización para el constructor del formulario:

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

El constructor de la plataforma, crea una baraja de cartas de 52, en orden desde el As de tréboles el rey de picas y el método de orden aleatorio
aleatoriamente el orden de las fichas de la baraja.

A continuación, agregué la lógica de código para el método Button1_Click, tal como se muestra en de figura 3. Para cada una de las dos manos, llamo al método de Deck.Deal para quitar dos tarjetas en el objeto de la baraja. A continuación, pasar esos dos tarjetas para el constructor de la mano y mostrar el valor de la mano en un control TextBox. Observe que el método Button1_Click controla cualquier excepción mediante un mensaje en el control ListBox.

La figura 3 de tratar las tarjetas

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);
  }
}

Hacer doble clic a continuación, en la ventana del Diseñador de Visual Studio en el control de button2 a controlador de eventos del control de generación automática
esqueleto. Se ha agregado código sencillo para comparar los dos objetos de la mano y mostrar un mensaje en el control ListBox. Tenga en cuenta que el método button2_Click no controla las excepciones directamente:

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("-------------------------");
}

El agente de inyección de errores

Antes de crear el instrumento de inyección de errores que se muestra en de figura 1, he descargado las DLL de la clave en mi equipo de host de prueba. Estos archivos DLL forman parte de una colección de bibliotecas de .NET denominada TestApi y se pueden encontrar en testapi.codeplex.com de .

La biblioteca de TestApi es una colección de utilidades de software de prueba relacionadas. En la biblioteca TestApi incluye un conjunto de API de inyección de error de código administrado. (Más información sobre ellas en blogs.msdn.com/b/ivo_manolov/archive/2009/11/25/9928447.aspx de .) Descargué la inyección de errores más reciente versión de API, que en mi caso, era la versión 0,4 y la descarga se han descomprimido. Explicaré lo que está en la descarga y la ubicación de los archivos binarios de inyección de errores en breve.

Versión 0,4 es compatible con las pruebas de las aplicaciones creadas con .NET Framework 3.5 de inyección de errores. La biblioteca de TestApi se está desarrollando la activa, por lo que debe comprobar el sitio de CodePlex para las actualizaciones de las técnicas que presento en este artículo. Además, quizás desee comprobar si hay actualizaciones y sugerencias sobre el blog de Bill Liu, el desarrollador principal de la biblioteca de inyección de errores de TestApi en blogs.msdn.com/b/billliu/ de .

Para crear el instrumento de inyección de errores inicia un nuevo proyecto de Visual Studio 2008 y se selecciona la plantilla de aplicación de consola de C#. Denominé FaultHarness de la aplicación y se ha agregado algún código mínimo para la plantilla de programa (consulte de figura 4).

La figura 4 de 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

Presionó la tecla de <F5> para generar y ejecutar el esqueleto de cables que crea una carpeta \Bin\Debug en la carpeta raíz de FaultHarness.

La descarga de TestApi tiene dos componentes clave. El primero es TestApiCore.dll, que se encuentra en la carpeta de archivos binarios de la descarga sin comprimir. Este archivo DLL copia en el directorio raíz de la aplicación FaultHarness. A continuación, I right-clicked en el proyecto FaultHarness en la ventana del explorador de soluciones, seleccionar Agregar referencia y al que señala TestApiCore.dll. A continuación, he agregado el uso de una instrucción para Microsoft.Test.FaultInjection a la parte superior de mi código de error de cables para que el código de agente no puede tener acceso directamente a la funcionalidad de TestApiCore.dll. También agregué un utilizando la instrucción de System.Diagnostics porque, como verá en breve, que desea tener acceso a las clases de ProcessStartInfo y procesos de ese espacio de nombres.

El segundo componente clave en la descarga de inyección de errores es una carpeta denominada FaultInjectionEngine. Esto incluye las versiones de 32 bits y 64 bits de FaultInjectionEngine.dll. Había copiado la carpeta de InjectionEngine de ­ de error completa en la carpeta que contiene mi FaultHarness ejecutable, en mi caso C:\FaultInjection\FaultHarness\bin\Debug\. La versión 0,4 del sistema de inyección de errores que estaba utilizando, requiere la carpeta FaultInjectionEngine en la misma ubicación que el ejecutable del instrumento. Además, el sistema requiere que la aplicación en archivos binarios de prueba se encuentra en la misma carpeta que el ejecutable del instrumento de, por lo que copian los archivos TwoCardPokerGame.exe y TwoCard ­ PokerLib.dll C:\FaultInjection\FaultHarness\bin\Debug\.

En resumen, cuando se utiliza el sistema de inyección de errores de TestApi, un buen método es generar un instrumento de esqueleto y ejecútela de para que un directorio \bin\debug de cables se crea, a continuación, coloque el archivo TestApiCore.dll en el directorio raíz de instrumento de colocar la carpeta FaultInjectionEngine en \bin\debug y coloque la aplicación sometida a prueba de archivos binarios (.exe y .dll) de \bin\debug también.

Utilizar el sistema de inyección de errores de TestApi, debe especificar la aplicación sometida a prueba, el método de la aplicación sometida a prueba de que se desencadenará un error, la condición que se desencadenará un error y el tipo de error que se activará:

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);

Tenga en cuenta que, debido a que el sistema requiere que la aplicación sometida a prueba en la misma carpeta que el ejecutable de cables, el nombre de la aplicación en el archivo ejecutable de la prueba no tiene el nombre de la ruta de acceso a su ubicación.

Especifica el nombre del método que se desencadenará el error insertado es un origen común de problemas para los principiantes de inyección de errores de TestApi. El nombre del método debe calificarse totalmente en el space.Class.Method(args) ­ de nombre de formulario. La técnica preferida es utilizar la herramienta ildasm.exe para examinar la aplicación sometida a prueba de que me ayude a determinar la firma del método de activación. Desde Visual Studio especial herramientas comandos shell, ejecutar ildasm.exe, seleccione la aplicación sometida a prueba, a continuación, haga doble clic en el método de destino. La figura 5, se muestra un ejemplo del uso de ildasm.exe para examinar la firma del método button2_Click.

Figure 5 Using ILDASM to Examine Method Signatures

La figura 5 de utilizando ILDASM para examinar las firmas de método

Cuando se especifica la firma de método de desencadenador, no utilice el tipo de valor devuelto del método y no utiliza nombres de parámetro. Para obtener la firma del método correcto a veces requiere un poco de ensayo y error. Por ejemplo, en mi primer intento para button2_Click, he utilizado:

TwoCardPokerGame.Form1.button2_Click(object,EventArgs)

Tuve que corregirlo para:

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

La descarga de TestApi contiene una carpeta de la documentación que contiene un documento de los conceptos que ofrece un buen procedimiento crear correctamente los distintos tipos de firmas de método, incluidos constructores, propiedades y métodos genéricos y operadores sobrecargados. Aquí un método que se encuentra en la aplicación sometida a prueba de destino, pero podría haber también dirigido un método en el de dos subyacente ­ CardPokerLib.dll, como:

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

Después de especificar el método de desencadenador, el siguiente paso es especificar la condición bajo la cual se insertará en la aplicación sometida a prueba el error. En mi ejemplo usé TriggerEveryOnNthCall(3), que como ha visto inserta cada tercera vez de un fallo el desencadenador se llama al método. El sistema de inyección de errores de TestApi tiene un conjunto ordenado de las condiciones del desencadenador incluido TriggerIfCalledBy(method) TriggerOnEveryCall y otros usuarios.

Después de especificar la condición de desencadenador, el siguiente paso es especificar el tipo de error que se insertarán en el sistema sometido a prueba. He usado BuiltInFaults.ThrowExceptionFault. Además de los errores de excepción, el sistema de inyección de errores de TestApi tiene errores de tipo de valor devuelto integrados que permiten insertar erróneos de los valores devueltos en la aplicación sometida a prueba en tiempo de ejecución. Por ejemplo, esto hará que el método de desencadenador devolver un valor -1 (posiblemente incorrecto):

IFault f = BuiltInFaults.ReturnValueFault(-1)

Después del método del desencadenador de error, condición y tipo de errores se han especificado, el siguiente paso es crear un nuevo FaultRule y pasa esa regla para un nuevo 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);

Con todas las preliminar en su lugar, la última parte de escribir el código de error de cables es iniciar mediante programación de la aplicación sometida a prueba en el entorno de sesiones de errores:

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

Cuando se ejecuta el agente de errores, se iniciará la aplicación sometida a prueba de la sesión de errores, con la de FaultInjection ­ Engine.dll ver situaciones donde en la que se llama al método de desencadenador cuando se cumple la condición de desencadenador. Las pruebas se realizan manualmente aquí, pero también puede ejecutar la automatización de prueba en una sesión de errores.

Mientras se ejecuta la sesión de errores, se registra la información acerca de la sesión en el directorio actual, es decir, el directorio que contiene el ejecutable del instrumento de errores y la aplicación en el archivo ejecutable de la prueba. Puede examinar estos archivos de registro para ayudar a resolver los problemas que pueden producirse mientras se está desarrollando el instrumento de inyección de errores.

Explicación

En el ejemplo de las explicaciones que he presentado aquí deben obtener funcionar con la creación de un instrumento de inyección de errores para su propia aplicación sometida a prueba. Al igual que con cualquier actividad que forma parte del proceso de desarrollo de software, se tienen recursos limitados y es recomendable analizar los costos y ventajas de realizar las pruebas de inyección de fallos. En el caso de algunas aplicaciones, el esfuerzo necesario para crear la probabilidad de inserción las pruebas que no sea la pena, pero hay muchas situaciones de pruebas en las pruebas de inyección de errores son de vital importancia. Imagínese software que controla un dispositivo médico o un sistema de vuelo. En casos como éstos, las aplicaciones absolutamente deben ser sólida y puede controlar correctamente todos los tipos de errores inesperados.

No hay un cierto irony implicados con inyección de prueba de errores. La idea es que, si puede prever las situaciones, cuando puede producir una excepción, es posible en teoría a menudo mediante programación protegerse de esa excepción y comprobar el funcionamiento correcto de ese comportamiento de protección. Sin embargo, incluso en estos casos, las pruebas de inyección de errores son útil para generar difíciles de crear las excepciones. Además, es posible insertar errores que son muy difíciles de anticipar, por ejemplo, la excepción System.OutOfMemoryException.

Las pruebas de inyección de errores está relacionado con y a veces se debe confundir con las pruebas de mutación. En las pruebas de mutación, deliberadamente insertar errores en el sistema sometido a prueba, pero, a continuación, ejecutar una serie de pruebas existente en el sistema defectuoso a fin de determinar si el conjunto de pruebas detecta los errores nuevos creados. Las pruebas de mutación son una forma de medir la efectividad del conjunto de programas de prueba y, finalmente, aumentar la cobertura de los casos de prueba. Como ha visto en este artículo, es el principal propósito de las pruebas de inyección de errores determinar si el sistema sometido a prueba controla correctamente los errores.

Dr.James McCaffrey trabaja en Volt Information Sciences Inc., donde encarga de formación técnica para los ingenieros de software que trabaja en el Microsoft Redmond, Washington, campus. Trabajó en varios productos de Microsoft, como Internet Explorer y MSN Search. El Dr. McCaffrey es el autor de “ .NET Test Automation Recipes ” (Apress, 2006) y puede ponerse en jammc@microsoft.com de .

Gracias a los siguientes expertos técnicos de este artículo: Bill Liu y de Paul Newson