Exportar (0) Imprimir
Expandir todo

Instrucciones para probar soluciones de Azure de forma eficiente

Actualizado: junio de 2014

Autor: Suren Machiraju

Revisores: Jaime Alva Bravo y Steve Wilkins

A veces, después de diseñar, codificar e implementar una solución de Microsoft Azure, descubre que no funciona. En este artículo se proporcionan instrucciones sobre cómo probar las aplicaciones de Microsoft Azure a lo largo de todo el ciclo de vida de implementación del software. El ámbito de las comprobaciones incluye la lógica de negocios y las pruebas completas de escenario de un extremo a otro. En este artículo se muestra cómo:

  • Desarrollar pruebas unitarias para los componentes de lógica de negocios y, al mismo tiempo, eliminar dependencias en los componentes de Microsoft Azure.

  • Diseñar pruebas de integración de un extremo a otro.

  • Eliminar sobrecargas en la instalación, la inicialización, la limpieza y el desmontaje de los recursos de servicios de Microsoft Azure (como, por ejemplo, colas) en cada serie de pruebas.

  • Eliminar la creación de infraestructuras duplicadas para espacios de nombres ACS, colas de Bus de servicio y otros recursos.

Además, este artículo proporciona información general sobre las diversas tecnologías y técnicas de comprobación que se pueden usar para probar aplicaciones de Microsoft Azure.

Estos son los cambios hechos en este artículo:

  1. Usamos Visual Studio 2013.

  2. Microsoft Fakes en Visual Studio 2013 reemplaza a Moles. Describimos esta característica en el artículo “Aislar el código probado con Microsoft Fakes”.

  3. Ahora, en Visual Studio 2013, está disponible una nueva versión de cobertura de código, que se invoca directamente desde el Explorador de pruebas. Para ver una descripción, consulte el artículo “Usar cobertura de código para determinar la cantidad de código que se está probando”.

  4. El equipo de Pex acaba de lanzar una versión ligera de Pex llamada Code Digger. Para ver una descripción, consulte el artículo “Microsoft Code Digger”.

  5. A partir de Visual Studio 2012, ya no recomendamos hacer pruebas de métodos privados. Para ver una explicación, consulte este artículo de opinión sobre las pruebas unitarias de métodos privados.

Hay dos clases de pruebas:

  • Pruebas unitarias: son pruebas centradas en un ámbito muy concreto que actúan sobre una sola función específica. Estas pruebas se denominan “código que se prueba” (CUT, Code Under Test). Debe quitar todas las dependencias que requiera el CUT.

  • Pruebas de integración: son pruebas más amplias que actúan sobre varias partes de la funcionalidad al mismo tiempo. En muchos casos, se asemejan a las pruebas unitarias, pero abarcan varias áreas de características e incluyen varias dependencias.

En general, estas pruebas se centran en crear y usar dobles de pruebas. Usamos los siguientes tipos de dobles de pruebas:

  • Emulaciones: son objetos simulados que implementan la misma interfaz que el objeto al que representan. Las emulaciones devuelven respuestas predefinidas. Cada emulación contiene un conjunto de códigos auxiliares de método y sirve de reemplazo de lo que crea mediante programación.

  • Códigos auxiliares: simulan el comportamiento de objetos de software.

  • Correcciones de compatibilidad (shims): le permiten aislar el código respecto de los ensamblados que no forman parte de su solución. También aíslan entre sí a los componentes de su solución.

Estas pruebas, una vez ejecutadas, pueden comprobar el estado y el comportamiento. El estado incluye, por ejemplo, el resultado que se obtiene cuando se llama a un método y devuelve un determinado valor. Un ejemplo de comportamiento sería llamar a un método en un orden determinado o un número determinado de veces.

Uno de los principales objetivos de las pruebas unitarias es eliminar las dependencias. En el marco de Azure, estas dependencias incluyen lo siguiente:

  • Colas de Bus de servicio.

  • Servicio de control de acceso.

  • Caché.

  • Tablas, blobs y colas de Azure.

  • Base de datos SQL de Azure.

  • Unidad de Azure (anteriormente, Unidad en la nube).

  • Otros servicios web.

Al crear pruebas para las aplicaciones de Azure, reemplazamos estas dependencias para que las pruebas se centren en poner en práctica la lógica.

Los ejemplos de las colas de Bus de servicio (incluidas las herramientas y las técnicas) que analizamos en este artículo también se aplican a todas las demás dependencias.

Si quiere implementar el marco de pruebas para sus aplicaciones de Microsoft Azure, necesitará:

  • Un marco de pruebas unitarias para definir y ejecutar las pruebas.

  • Un marco de simulación para que le sea más fácil aislar las dependencias y crear pruebas unitarias con ámbitos específicos.

  • Herramientas que ayuden a generar automáticamente pruebas unitarias y, así, aumentar la cobertura de código.

  • Otros marcos que puedan ayudar con los diseños que se pueden probar, que aprovechen la inserción de dependencias y apliquen el patrón de inversión de control (IoC).

Visual Studio incluye una utilidad de línea de comandos, llamada MS Test, para ejecutar pruebas unitarias creadas en Visual Studio. Visual Studio también incluye un conjunto de plantillas de elementos y proyectos para ayudar con las pruebas. Normalmente, se crea un proyecto de prueba y luego se agregan clases (denominadas accesorios de prueba) complementadas con el atributo [TestClass]. Las clases contienen métodos complementados con el atributo [TestMethod]. En MS Test, las distintas ventanas de Visual Studio le permiten ejecutar las pruebas unitarias definidas en el proyecto. También puede revisar los resultados después de ejecutarlas.

noteNota
Las ediciones de Visual Studio 2013 Express, Professional y Test Professional no contienen MS Test.

En MS Test, las pruebas unitarias siguen el patrón AAA: administración, actuación y aserción.

  • Administración: crear todos los objetos y las configuraciones que sean requisitos previos, y todas las demás condiciones previas y los datos que necesite el CUT.

  • Actuación: realizar la prueba en sí, dirigida a un ámbito específico, sobre el código.

  • Aserción: comprobar que se obtuvieron los resultados esperados.

Las bibliotecas del marco de MS Test incluyen las clases auxiliares PrivateObject y PrivateType. Estas clases utilizan reflexión para que resulte fácil invocar a miembros de instancias no públicas o miembros estáticos desde dentro del código de la prueba unitaria.

Las ediciones Premium y Ultimate de Visual Studio contienen herramientas de pruebas unitarias mejoradas. Estas herramientas se integran con MS Test. Con las herramientas, también puede analizar la cantidad de código sobre el que actúan sus pruebas unitarias. Además, las herramientas aplican un código de colores al código fuente para indicar la cobertura. Esta característica se llama cobertura de código.

Uno de los objetivos de las pruebas unitarias es realizar pruebas con aislamiento. Sin embargo, ocurre a menudo que el código que se prueba no se puede comprobar de forma aislada. A veces nos encontramos con que, cuando se escribió el código, no se pensó en la capacidad de someterlo a pruebas. Sería difícil reescribir el código, porque está basado en otras bibliotecas que no son fáciles de aislar. Por ejemplo, el código que interactúa con entornos externos no se puede aislar con facilidad. Usar un marco de simulación le permite aislar los dos tipos de dependencias.

En la sección de vínculos que hay al final de este artículo, encontrará una lista de marcos de simulación que puede usar. Este artículo se centra en cómo utilizar Microsoft Fakes.

Con Microsoft Fakes puede aislar el código que va a probar, reemplazando otras partes de la aplicación por códigos auxiliares o correcciones de compatibilidad. Se trata de pequeños fragmentos de código controlados por sus pruebas. Al aislar el código para realizar pruebas, puede estar seguro de que, si la prueba no se supera, el motivo está ahí y no en otro lugar. Además, con los códigos auxiliares y las correcciones de compatibilidad, puede probar el código aunque haya otras partes de la aplicación que no funcionen todavía.

Las emulaciones pueden ser de dos tipos:

  • Un código auxiliar reemplaza una clase por un pequeño sustituto que implementa la misma interfaz. Para poder usar códigos auxiliares, tiene que diseñar la aplicación de modo que cada componente dependa solamente de interfaces y no de otros componentes. Entendemos por “componente” una clase o un grupo de clases que se diseñan y se actualizan juntos y que, en general, están incluidos en un ensamblado.

  • Una corrección de compatibilidad modifica el código compilado de la aplicación en tiempo de ejecución. En lugar de llamar a un método especificado, la aplicación ejecuta el código de la corrección de compatibilidad que proporcione su prueba. Puede usar correcciones de compatibilidad para reemplazar las llamadas a ensamblados que no pueda modificar como, por ejemplo, ensamblados .NET.

Code Digger analiza las posibles rutas de acceso de ejecución de su código .NET. El resultado es una tabla en la que cada fila muestra un comportamiento único del código. La tabla permite entender cómo se comporta el código y también puede revelar errores ocultos.

Para analizar el código en el editor de Visual Studio, utilice el nuevo elemento del menú contextual Generar tabla de entradas/salidas para invocar a Code Digger. Code Digger calcula y muestra los pares de entrada-salida. Code Digger busca sistemáticamente errores, excepciones y errores de aserción.

De forma predeterminada, Code Digger solo funciona con código .NET público que se encuentre en bibliotecas de clases portables. Más adelante en este artículo, examinaremos cómo configurar Code Digger para explorar otros proyectos de .NET.

Code Digger utiliza el motor Pex y el solucionador de restricciones Z3 de Microsoft Research para analizar de forma sistemática todas las ramas del código. Code Digger trata de generar un conjunto de aplicaciones de prueba que logre una gran cobertura de código.

Puede usar Microsoft Unity para los contenedores de inversión de control (IoC) e inserción de dependencias (DI) extensible. Admite intercepción, inserción de constructores, inserción de propiedades e inserción de llamadas de método. Con Microsoft Unity y otras herramientas similares, podrá crear diseños que se puedan probar y que le permitan insertar sus dependencias en todos los niveles de la aplicación. (Damos por supuesto que su aplicación se diseñó y se creó teniendo presentes la inserción de dependencias y uno de estos marcos).

Estos marcos son ideales para escribir código que se pueda probar y, en última instancia, para escribirlo correctamente. Sin embargo, al principio pueden tener requisitos de diseño bastante estrictos. En este artículo, no analizaremos los contenedores de IoC ni DI.

En esta sección, describimos una solución que incluye un sitio web hospedado en un rol web. El sitio web inserta mensajes en una cola. Luego, un rol de trabajo procesa los mensajes de la cola. Nos interesa probar los aspectos de los tres elementos.

Imagine que tiene un sitio web que crea pedidos, y una cola de Bus de servicio crea una cola para procesar estos pedidos. La página web es similar a la de la Ilustración 1:

Ilustración 1

Cuando el usuario hace clic en Create, la cola de Bus de servicio publica el nuevo pedido en la acción Create del controlador asociado. La acción se implementa así:

noteNota
La clase MicrosoftAzureQueue es una clase contenedora que utiliza las API de .NET de Bus de servicio (como MessageSender y MessageReceiver) para interactuar con las colas de Bus de servicio.

private IMicrosoftAzureQueue queue;
public OrderController()
{
    queue = new MicrosoftAzureQueue();
}
public OrderController(IMicrosoftAzureQueue queue)
{
    this.queue = queue;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "OrderId,Description")] Order order)
{
    try
    {
        if (ModelState.IsValid)
        {
            string connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
            string queueName = "ProcessingQueue";
            queue.InitializeFromConnectionString(connectionString, queueName);
            queue.Send(order);
        }
        return View("OrderCreated");
    }
    catch (Exception ex)
    {
        Trace.TraceError(ex.Message);
        return View("Error");
    }            
}

El código recupera la configuración de CloudConfigurationManager y envía a la cola un mensaje que contiene el pedido. Tenga en cuenta que la acción Create utiliza los siguientes métodos:

  • InitializeFromConnectionString (cadena ConnectionString, cadena QueueName)

  • Send (clase MicrosoftAzureQueue)

Nos interesa crear desvíos para esos métodos utilizando Fakes para controlar su comportamiento y quitar las dependencias en el entorno real. Al usar Fakes, ya no es necesario ejecutar las pruebas en el emulador de Azure ni llamar a la cola de Bus de servicio. Ejecutamos la acción Create en el controlador para comprobar que la entrada de Order es la que se envió a la cola. El método Send comprueba que la entrada tiene los valores de Order Id y Description que se introdujeron en la acción. Luego, se asegura de que aparezca como resultado la vista OrderCreated.

Es muy fácil crear una prueba unitaria para la implementación anterior con Fakes. Dentro del proyecto de prueba, haga clic con el botón secundario en el ensamblado que contiene los tipos que quiere simular. Después, elija Agregar ensamblado de Fakes.

En el ejemplo, seleccionamos Microsoft.ServiceBus y elegimos Agregar ensamblado de Fakes. Se agregará al proyecto de prueba un archivo XML con el nombre “Microsoft.ServiceBus.Fakes”. Haga lo mismo con el ensamblado Microsoft.WindowsAzure.Configuration.

Al crear el proyecto de prueba, se agregan referencias a las versiones simuladas generadas automáticamente de estos ensamblados. En el ejemplo, los ensamblados generados son “Microsoft.ServiceBus.Fakes” y “Microsoft.WindowsAzure.Configuration”.

Cree un método de prueba unitaria y aplique el atributo [TestCategory("With fakes")]. Dentro de la prueba unitaria, deberá utilizar correcciones de compatibilidad para aislar entre sí las partes de la aplicación.

Uso de correcciones de compatibilidad para aislar la aplicación de otros ensamblados y realizar pruebas unitarias

Las correcciones de compatibilidad (shims) son una de las dos tecnologías que proporciona el marco Microsoft Fakes para que pueda aislar fácilmente del entorno los componentes que se someten a pruebas. Las correcciones de compatibilidad desvían las llamadas a determinados métodos y las dirigen al código que se escribe como parte de la prueba. Muchos métodos devuelven resultados distintos que varían según condiciones externas. Sin embargo, en el caso de las correcciones de compatibilidad, es su prueba la que las controla. Por eso, pueden devolver los mismos resultados llamada tras llamada. Así, es mucho más sencillo escribir las pruebas. Utilice correcciones de compatibilidad para aislar el código respecto de los ensamblados que no forman parte de su solución. Para aislar los componentes de su solución entre sí, le recomendamos que use códigos auxiliares.

Uso de códigos auxiliares para aislar entre sí las partes de la aplicación y realizar pruebas unitarias

Los códigos auxiliares son una de las dos tecnologías que proporciona el marco Microsoft Fakes para que pueda aislar fácilmente los componentes que se someten a pruebas respecto de los demás componentes a los que llama. Un código auxiliar es un pequeño fragmento de código que sustituye a otro componente durante las pruebas. La ventaja de usar un código auxiliar es que devuelve resultados coherentes, por lo que resulta más fácil escribir las pruebas. Además, puede ejecutar pruebas aunque los demás componentes no funcionen todavía.

En nuestro caso de prueba, usaremos correcciones de compatibilidad para CloudConfigurationManager y BrokeredMessage de los ensamblados de Azure. Utilizaremos códigos auxiliares para MicrosoftAzureQueue, que es una clase de nuestra solución.

[TestMethod]
[TestCategory("With fakes")]
public void Test_Home_CreateOrder()
{
    // Shims can be used only in a ShimsContext
    using (ShimsContext.Create())
    {
        // Arrange
        // Use shim for CloudConfigurationManager.GetSetting
        Microsoft.WindowsAzure.Fakes.ShimCloudConfigurationManager.GetSettingString = (key) =>
        {
            return "mockedSettingValue";
        };
                
        // Create the fake queue:
        // In the completed application, queue would be a real one:
        bool wasCreateFromConnString = false;
        Order orderSent = null;
        IMicrosoftAzureQueue queue =
                new OrderWebRole.Queue.Fakes.StubIMicrosoftAzureQueue() // Generated by Fakes.
                {
                    // Define each method:
                    // Name is original name + parameter types:
                    InitializeFromConnectionStringStringString = (connectionString, queueName) => {
                        wasCreateFromConnString = true;
                    },
                    SendOrder = (order) => {
                    orderSent = order;
                    }
                };

        // Component under test
        OrderController controller = new OrderController(queue);

        // Act
        Order inputOrder = new Order()
        {
            OrderId = System.Guid.NewGuid(),
            Description = "A mock order"
        };
        ViewResult result = controller.Create(inputOrder) as ViewResult;

        //Assert
        Assert.IsTrue(wasCreateFromConnString);
        Assert.AreEqual("OrderCreated", result.ViewName);
        Assert.IsNotNull(orderSent);
        Assert.AreEqual(inputOrder.OrderId, orderSent.OrderId);
        Assert.AreEqual(inputOrder.Description, orderSent.Description);
    }
}

El rol web se ocupa de agregar un pedido a la cola. Ahora, piense en cómo se podría probar un rol de trabajo que procesara los pedidos recuperándolos de la cola de Bus de servicio. El método Run de nuestro rol de trabajo sondea periódicamente la cola para ver si hay pedidos y procesarlos.

private IMicrosoftAzureQueue queue;
public WorkerRole()
{
    queue = new MicrosoftAzureQueue();
}

public WorkerRole(IMicrosoftAzureQueue queue)
{
    this.queue = queue;
}
public override void Run()
{
    try
    {
        string connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
        string queueName = "ProcessingQueue";
               
        queue.InitializeFromConnectionString(connectionString, queueName);
            
        queue.CreateQueueIfNotExists();
              
        while (true)
        {
            Thread.Sleep(2000);
            //Retrieve order from Service Bus Queue  
            TryProcessOrder(queue);
        }
    }
    catch (Exception ex)
    {
        if (queue != null)
            queue.Close();
        System.Diagnostics.Trace.TraceError(ex.Message);
    }
}

Necesitamos una prueba con la que comprobar que esta rutina recupera un mensaje correctamente. A continuación, puede ver la prueba unitaria completa correspondiente al método Run del rol de trabajo.

[TestMethod]
public void Test_WorkerRole_Run()
{
    // Shims can be used only in a ShimsContext:
    using (ShimsContext.Create())
    {
        Microsoft.WindowsAzure.Fakes.ShimCloudConfigurationManager.GetSettingString = (key) =>
        {
            return "mockedSettingValue";
        };

        // Arrange 
        bool wasEnsureQueueExistsCalled = false;
        int numCallsToEnsureQueueExists = 0;

        // Create the fake queue:
        // In the completed application, queue would be a real one:
        bool wasConnectionClosedCalled = false;
        bool wasCreateFromConnString = false;
        bool wasReceiveCalled = false;
        int numCallsToReceive = 0;

        bool wasCompleteCalled = false;
        int numCallsToComplete = 0;
        IMicrosoftAzureQueue queue =
                new OrderWebRole.Queue.Fakes.StubIMicrosoftAzureQueue() // Generated by Fakes.
                {

                    // Define each method:
                    // Name is original name + parameter types:
                    InitializeFromConnectionStringStringString = (connectionString, queueName) =>
                    {
                        wasCreateFromConnString = true;
                    },
                    CreateQueueIfNotExists = () =>
                    {
                        wasEnsureQueueExistsCalled = true;
                        numCallsToEnsureQueueExists++;
                    },
                    Receive = () =>
                {
                    wasReceiveCalled = true;
                    if (numCallsToReceive >= 3) throw new Exception("Aborting Run");
                    numCallsToReceive++;
                    Order inputOrder = new Order()
                    {
                        OrderId = System.Guid.NewGuid(),
                        Description = "A mock order"
                    };
                    return new BrokeredMessage(inputOrder);
                },
                    Close = () =>
                    {
                        wasConnectionClosedCalled = true;
                    }

                };


        Microsoft.ServiceBus.Messaging.Fakes.ShimBrokeredMessage.AllInstances.Complete = (message) =>
        {
            wasCompleteCalled = true;
            numCallsToComplete++;
        };

        WorkerRole workerRole = new WorkerRole(queue);

        //Act
        workerRole.Run();

        //Assert
        Assert.IsTrue(wasCreateFromConnString);
        Assert.IsTrue(wasConnectionClosedCalled);
        Assert.IsTrue(wasEnsureQueueExistsCalled);
        Assert.IsTrue(wasReceiveCalled);
        Assert.AreEqual(1, numCallsToEnsureQueueExists);
        Assert.IsTrue(numCallsToReceive > 0);
        Assert.IsTrue(wasCompleteCalled);
        Assert.IsTrue(numCallsToComplete > 0);
        Assert.AreEqual(numCallsToReceive, numCallsToComplete);

    }
}

Deberá establecer el delegado de la propiedad AllInstances del tipo de emulación generado. Al usar el delegado, todas las instancias que cree del tipo de emulación real se desviarán a los métodos para los que definió delegados.

En el ejemplo, queremos utilizar el método Run de la instancia original, pero proporcionar desvíos para los métodos de las instancias CreateQueue y TryProcessOrder. En el código, iniciamos una excepción para salir del bucle infinito que mantiene el método Run a una hora predeterminada.

Puede que se pregunte por qué no nos limitamos a usar directamente MessageSender/MessageReceiver y las clases relacionadas del SDK de Bus de servicio, en lugar de insertar un tipo auxiliar. Si queremos aislar por completo el código, de modo que no llame al Bus de servicio real, hay dos opciones:

  • Escribir emulaciones que hereden las clases abstractas en el espacio de nombres Microsoft.ServiceBus.

  • Dejar que las emulaciones creen tipos simulados para todas ellas.

El problema con estos dos métodos es la complejidad. En definitiva, con los dos verá una reflexión en clases como TokenProvider y QueueClient. La reflexión provoca los siguientes problemas:

  • Debe crear tipos derivados de estos tipos abstractos que expongan todas las invalidaciones necesarias.

  • Debe exponer los tipos internos en los que se basen, en la práctica, las versiones reales de estas clases.

  • Con los tipos internos, debe volver a crear sus constructores o patrones de diseño Factory Method de forma inteligente para eliminar la dependencia del Bus de servicio real.

La mejor opción es insertar su propio tipo auxiliar. Es todo lo que necesita para simular, desviar y aislar el código respecto del Bus de servicio real.

Para analizar qué comprobaron estas pruebas unitarias, podemos examinar los datos de cobertura de código. Si ejecutamos las dos pruebas unitarias con MS Test, veremos que se superan. También veremos los detalles relacionados de la ejecución en el diálogo Explorador de pruebas.

Ilustración 2

Puede ejecutar la cobertura de código de sus pruebas desde el Explorador de pruebas. Haga clic con el botón secundario en la prueba y elija Analizar cobertura de código para las pruebas seleccionadas. Los resultados aparecerán en la ventana Resultados de la cobertura de código. Para activar la recolección de datos para la cobertura de código, configure el archivo Local.testsettings en Elementos de la solución. Al abrir este archivo, se inicia el editor Configuración de pruebas.

Si tiene una solución que no incluye el archivo Local.testsettings, agréguelo a la solución con el siguiente procedimiento.

  1. Haz clic en el botón Agregar nuevo elemento.

  2. Seleccione Prueba y luego haga clic en Configuración de pruebas.


    Ilustración 3

  3. Haga clic en la pestaña Datos y diagnósticos y active la casilla que hay a la derecha de la fila Cobertura de código.

  4. Luego, haga clic en el botón Configurar .

  5. En la ventana Detalle de cobertura de código, seleccione todos los ensamblados que probará y haga clic en Aceptar.


    Ilustración 4

  6. Para descartar el editor Configuración de pruebas, haga clic en Aplicar y Cerrar.

  7. Vuelva a ejecutar las pruebas y haga clic en el botón Cobertura de código. Debería ver unos resultados de cobertura de código similares a los de la Ilustración 5.


    Ilustración 5

  8. Haga clic en el icono Mostrar colores en cobertura de código y luego navegue hacia abajo hasta un método de la cuadrícula.

  9. Haga doble clic en el método. Su código fuente tendrá un color que indique las áreas que se probaron. El color verde indica que el código se probó. El gris indica que el código se probó parcialmente o no se probó.


    Ilustración 6

Al crear pruebas unitarias manualmente se obtienen resultados muy valiosos, pero Code Digger también puede ayudarle a actualizar sus pruebas unitarias de manera inteligente. Lo hace al probar con valores de parámetros en los que tal vez usted no haya pensado. Después de instalar Code Digger, para explorar un método, puede hacer clic con el botón secundario en el método dentro del editor de código y luego elegir Generar tabla de entradas/salidas.

Ilustración 7

Pasado un momento, Code Digger acabará de procesar los datos y los resultados se estabilizarán. Por ejemplo, la Ilustración 8 muestra los resultados que se obtienen al ejecutar Code Digger sobre el método TryProcessOrder del rol de trabajo. Observe que Code Digger consiguió crear una prueba que provocó una excepción. Code Digger muestra también las entradas que creó para generar esa excepción (un valor nulo como parámetro de la cola), lo que es incluso más importantes.

Ilustración 8

Mostrar:
© 2014 Microsoft