Compartir a través de


Creación de pruebas unitarias a partir de ejecuciones de flujo de trabajo estándar en Azure Logic Apps con Visual Studio Code

Se aplica a: Azure Logic Apps (estándar)

Las pruebas unitarias son una práctica esencial que mantiene la aplicación o solución confiable y precisa durante todo el ciclo de vida de desarrollo de software. Las pruebas unitarias le ayudan a validar de forma eficaz y sistemática los componentes clave de la solución.

En el caso de los flujos de trabajo de aplicaciones lógicas estándar, puede crear pruebas unitarias mediante Visual Studio Code y la extensión Azure Logic Apps (Estándar). Esta funcionalidad le permite usar ejecuciones de flujo de trabajo ejecutadas previamente para crear pruebas unitarias y adaptarlas a escenarios admitidos por la solución de aplicación lógica. Este enfoque proporciona las ventajas siguientes:

  • Reutilice las ejecuciones del flujo de trabajo para generar datos simulados para operaciones específicas dentro del flujo de trabajo.

    Estos datos le permiten probar flujos de trabajo sin tener que llamar a servicios externos, sistemas o API. Ahorra tiempo y el flujo de trabajo permanece alineado con el escenario de ejecución real del flujo de trabajo.

  • Mejore la calidad del flujo de trabajo mediante la identificación y la resolución de posibles problemas antes de implementar en otros entornos.

  • Optimice la integración de pruebas unitarias con el proceso de desarrollo, a la vez que garantiza un comportamiento de flujo de trabajo coherente y preciso.

En esta guía se muestra cómo crear una definición de prueba unitaria a partir de una ejecución de flujo de trabajo. Esta definición simula las llamadas externas de cada operación de flujo de trabajo sin cambiar la lógica del flujo de trabajo. Al crear una prueba unitaria a partir de una ejecución de flujo de trabajo, obtendrá un proyecto de prueba unitaria que incluya dos carpetas:

  • Una carpeta que contiene clases fuertemente tipadas para cada operación simulable de su proceso.

  • Carpeta para cada definición de prueba unitaria, que incluye los siguientes archivos:

    • Un archivo JSON que representa las operaciones simuladas generadas en el flujo de trabajo.

    • Un archivo de C# que contiene una clase de ejemplo y métodos que usa para configurar sus propias aserciones, confirme que el flujo de trabajo se comporta según lo esperado y asegúrese de que el flujo de trabajo se comporta de forma confiable y predecible en el ecosistema de Azure más grande.

Prerrequisitos

Limitaciones y problemas conocidos

  • Actualmente, esta versión solo admite C# para crear pruebas unitarias.

  • Esta versión no admite acciones no simuladas. Asegúrese de que todas las acciones de la ruta de acceso de ejecución del flujo de trabajo se simulan.

  • Esta versión no admite los siguientes tipos de acción:

    • Acciones de la cuenta de integración
    • Acciones del mapeador de datos
    • Acciones de código personalizadas
    • Acciones XML
    • Acciones líquidas
    • Acciones de codificación y descodificación de EDI

Revisión de los conceptos básicos

En la lista siguiente se incluyen conceptos básicos pero importantes sobre las pruebas unitarias para flujos de trabajo estándar:

  • Prueba unitaria de aplicación lógica

    Ejecución controlada del flujo de trabajo que inserta objetos ficticios. Estos objetos representan el desencadenador de flujo de trabajo o las acciones que dependen de servicios o sistemas externos.

  • Acción simulable

    Acción de flujo de trabajo que depende de un servicio o sistema externo. Puede convertir estas acciones en acciones simuladas para la creación y ejecución de pruebas unitarias.

Creación de una prueba unitaria a partir de una ejecución de flujo de trabajo

  1. En Visual Studio Code, abra el proyecto de aplicación lógica estándar.

  2. En la barra de herramientas de Visual Studio Code, en el menú Ejecutar, seleccione Iniciar depuración. (Teclado: presione F5)

  3. Vuelva a la ventana Explorador . En el proyecto, expanda la carpeta de definición de flujo de trabajo.

  4. Abra el menú contextual workflow.json y seleccione Información general.

  5. En la página de información general, en el Historial de ejecuciones, seleccione la ejecución del flujo de trabajo que se usará para crear una prueba unitaria.

    Captura de pantalla que muestra Visual Studio Code con el proyecto de aplicación lógica estándar, el modo de depuración en ejecución, la página de información general del flujo de trabajo abierto y la ejecución de flujo de trabajo seleccionada.

  6. En la barra de herramientas del historial de ejecución, seleccione Crear prueba unitaria desde la ejecución.

    Captura de pantalla que muestra Visual Studio Code, página historial de ejecución de flujo de trabajo estándar y comando seleccionado para crear pruebas unitarias.

  7. Proporcione un nombre que se usará para la prueba unitaria, la clase de prueba unitaria y el archivo de C#.

    En la ventana Explorador , aparece una nueva carpeta de proyecto denominada Pruebas en la carpeta del proyecto de aplicación lógica. La carpeta Tests contiene las siguientes carpetas y archivos:

    Captura de pantalla que muestra Visual Studio Code, proyecto de aplicación lógica estándar y carpeta Pruebas con carpetas y archivos de pruebas unitarias.

    Archivo o carpeta Descripción
    Tests
    || <logic-app-name>
    En la carpeta Tests, una carpeta <logic-app-name> aparece al agregar pruebas unitarias a un proyecto de Logic App.
    Tests
    || <logic-app-name>
    ||| <workflow-name>
    En la carpeta <logic-app-name>, aparece una carpeta <workflow-name> al agregar pruebas unitarias para un flujo de trabajo.
    Tests
    || <logic-app-name>
    ||| <workflow-name>
    |||| MockOutputs
    < operation-name-outputs >||||| .cs
    En la carpeta <workflow-name>, dentro de la carpeta MockOutputs se encuentra un archivo de C# (.cs) con clases con tipado fuerte para cada operación del conector en el flujo de trabajo. Cada .cs nombre de archivo usa el siguiente formato:

    < operation-name >[Trigger\|Action]Output.cs

    Si una operación de conector tiene contratos dinámicos, aparece una clase para cada tipo dinámico. Un tipo dinámico hace referencia a un parámetro de operación que tiene entradas y salidas diferentes en función del valor proporcionado para ese parámetro. Puede usar estas clases para ampliar las pruebas unitarias y crear nuevos simulacros desde cero.
    Tests
    || <logic-app-name>
    ||| <workflow-name>
    |||| <unit-test-name>
    ||||| <unit-test-name>-mock.json
    ||||| <unit-test-name>.cs
    En la <workflow-name> carpeta , la <unit-test-name> carpeta contiene los siguientes archivos:

    El <unit-test-name>-mock.json archivo contiene una representación JSON de los simulacros generados, basada en la ejecución del flujo de trabajo que originó la creación de la prueba unitaria.

    : el <unit-test-name>.cs archivo contiene una clase y métodos de C# de ejemplo que usan el *-mock.json archivo para ejecutar y declarar los resultados. Puede editar este archivo para que coincida con sus escenarios de prueba específicos.

Revise el archivo *-mock.json

Este archivo tiene las siguientes secciones principales:

Sección triggerMocks

La triggerMocks sección contiene el resultado ficticio del desencadenador de flujo de trabajo. Esta sección es necesaria para iniciar la ejecución del flujo de trabajo, como se muestra en el ejemplo siguiente:

{
    "triggerMocks": {
        "When_messages_are_available_in_a_queue_(peek-lock)": {
            "name": "When_messages_are_available_in_a_queue_(peek-lock)",
            "status": "Succeeded",
            "outputs": {
                "body": {
                    "contentData": {
                        "messageId": "1234",
                        "status": "new",
                        "contentType": "application/json",
                        "userProperties": {},
                        "scheduledEnqueueTimeUtc": "1/1/0001 12:00:00 AM",
                        "timeToLive": "14.00:00:00",
                        "deliveryCount": 1,
                        "enqueuedSequenceNumber": 0,
                        "enqueuedTimeUtc": "2025-04-07T01:10:09.738Z",
                        "lockedUntilUtc": "2025-04-07T01:11:09.769Z",
                        "lockToken": "78232fa8-03cf-4baf-b1db-3375a64e0ced",
                        "sequenceNumber": 5
                    }
                }
            }
        }
    },
    "actionMocks": {...}
}

Sección actionMocks

Para cada acción simulable de una ejecución de flujo de trabajo, la actionMocks sección contiene una acción simulada y garantiza la ejecución controlada del flujo de trabajo.

{
    "triggerMocks": {...},
    "actionMocks": {
        "Call_External_API": {
            "name": "Call_External_API",
            "status": "Succeeded",
            "outputs": {
                "statusCode": 200,
                "body": {
                    "status": "Awesome!"
                }
            }
        },
        "CompleteMessage": {
            "name": "CompleteMessage",
            "status": "Succeeded",
            "outputs": {
                "statusCode": "OK",
                "body": {}
            }
        }
    }
}

Revise el archivo de prueba unitaria *.cs

Esta clase de prueba unitaria proporciona un marco para probar flujos de trabajo de aplicaciones lógicas estándar simulando desencadenadores y acciones. Esta clase le permite probar flujos de trabajo sin llamar realmente a servicios externos o API.

Estructura de clase de prueba

Una clase de prueba unitaria típica usa la estructura siguiente:

[TestClass]
public class <unit-test-name>
{
    public TestExecutor TestExecutor;

    [TestInitialize]
    public void Setup()
    {
        this.TestExecutor = new TestExecutor("<workflow-name>/testSettings.config");
    }

    // Add test methods here.

    // Add helper methods here.
}

Método Setup()

Este método crea una instancia de la TestExecutor clase mediante la ruta de acceso al archivo de configuración de configuración de la prueba. El método se ejecuta antes de cada ejecución de prueba y crea una nueva instancia de TestExecutor.

[TestInitialize]
public void Setup()
{
    this.TestExecutor = new TestExecutor("<workflow-name>/testSettings.config");
}

Métodos de prueba de ejemplo

En la sección siguiente se describen los métodos de prueba de ejemplo que puede usar en la clase de prueba unitaria.

Prueba estática de datos ficticios

El método siguiente muestra cómo usar datos ficticios estáticos para probar el flujo de trabajo. En este método, puede completar las siguientes tareas:

  • Establezca los valores de propiedad en las acciones simuladas.
  • Ejecute el flujo de trabajo con los datos ficticios configurados.
  • Confirme que la ejecución se realizó correctamente.
[TestMethod]
public async Task <workflow-name>_<unit-test-name>_ExecuteWorkflow_SUCCESS_Sample1()
{
    // PREPARE mock: Generate mock action and trigger data.
    var mockData = this.GetTestMockDefinition();
    var sampleActionMock = mockData.ActionMocks["Call_External_API"];
    sampleActionMock.Outputs["your-property-name"] = "your-property-value";

    // ACT: Create the UnitTestExecutor instance. Run the workflow with mock data.
    var testRun = await this.TestExecutor
        .Create()
        .RunWorkflowAsync(testMock: mockData).ConfigureAwait(continueOnCapturedContext: false);

    // ASSERT: Confirm successful workflow execution and that the status is 'Succeeded'.
    Assert.IsNotNull(value: testRun);
    Assert.AreEqual(expected: TestWorkflowStatus.Succeeded, actual: testRun.Status);
}

Prueba dinámica de datos ficticios

El método siguiente muestra cómo usar datos ficticios dinámicos con métodos de devolución de llamada. Este enfoque proporciona dos opciones que generan dinámicamente datos ficticios:

Ambos enfoques permiten crear respuestas dinámicas basadas en el contexto de ejecución de pruebas unitarias.

[TestMethod]
public async Task <workflow-name>_<unit-test-name>_ExecuteWorkflow_SUCCESS_Sample2()
{
    // PREPARE: Generate mock action and trigger data.
    var mockData = this.GetTestMockDefinition();
    
    // OPTION 1: Define a callback class.
    mockData.ActionMocks["Call_External_API"] = new CallExternalAPIActionMock(
        name: "Call_External_API", 
        onGetActionMock: CallExternalAPIActionMockOutputCallback);

    // OPTION 2: Define an inline lambda function.
    mockData.ActionMocks["Call_External_API"] = new CallExternalAPIActionMock(
        name: "Call_External_API", 
        onGetActionMock: (testExecutionContext) =>
        {
            return new CallExternalAPIActionMock(
                status: TestWorkflowStatus.Succeeded,
                outputs: new CallExternalAPIActionOutput {

                    // If this account contains a JObject Body, 
                    // set the properties you want here:
                    // Body = "something".ToJObject()

                }
            );
        });
        
    // ACT: Create UnitTestExecutor instance. Run the workflow with mock data.
    var testRun = await this.TestExecutor
        .Create()
        .RunWorkflowAsync(testMock: mockData).ConfigureAwait(continueOnCapturedContext: false);

    // ASSERT: Confirm successful workflow execution and that the status is 'Succeeded'.
    Assert.IsNotNull(value: testRun);
    Assert.AreEqual(expected: TestWorkflowStatus.Succeeded, actual: testRun.Status);
}

Prueba del escenario de error

En el método siguiente se muestra cómo probar las condiciones de error. En este método, puede completar las siguientes tareas:

  • Configurar acciones simuladas para fallar con códigos de error y mensajes específicos.
  • Confirme que el flujo de trabajo controla estas condiciones de error correctamente.
[TestMethod]
public async Task <workflow-name>_<unit-test-name>_ExecuteWorkflow_FAILED_Sample3()
{
    // PREPARE: Generate mock action and trigger data.
    var mockData = this.GetTestMockDefinition();
    var mockError = new TestErrorInfo(code: ErrorResponseCode.BadRequest, message: "Input is invalid.");
    mockData.ActionMocks["Call_External_API"] = new CallExternalAPIActionMock(
        status: TestWorkflowStatus.Failed, 
        error: mockError);

    // ACT: Create UnitTestExecutor instance. Run the workflow with mock data.
    var testRun = await this.TestExecutor
        .Create()
        .RunWorkflowAsync(testMock: mockData).ConfigureAwait(continueOnCapturedContext: false);

    // ASSERT: Confirm successful workflow execution and that the status is 'Succeeded'.
    Assert.IsNotNull(value: testRun);
    Assert.AreEqual(expected: TestWorkflowStatus.Failed, actual: testRun.Status);
}

Métodos auxiliares

En la sección siguiente se describen los métodos de prueba usados por los métodos de prueba de ejemplo. Los métodos auxiliares aparecen en los métodos de prueba de la definición de clase.

GetTestMockDefinition()

El método siguiente carga la definición ficticia desde un archivo JSON. Puede editar este método si los datos simulados se almacenan en una ubicación o formato diferente.

private TestMockDefinition GetTestMockDefinition()
{
    var mockDataPath = Path.Combine(TestExecutor.rootDirectory, "Tests", TestExecutor.logicAppName, 
        TestExecutor.workflow, "<unit-test-name>", "<unit-test-name>-mock.json");
    return JsonConvert.DeserializeObject<TestMockDefinition>(File.ReadAllText(mockDataPath));
}

Método de devolución de llamada

El método siguiente genera dinámicamente datos ficticios. El nombre del método varía en función del nombre de la acción simulada en los métodos de prueba para los datos ficticios estáticos o dinámicos. Puede editar este método para devolver diferentes respuestas ficticias en función de los requisitos del escenario de prueba o usarla como plantilla para crear sus propios métodos de devolución de llamada dinámicos.

public CallExternalAPIActionMock CallExternalAPIActionMockOutputCallback(TestExecutionContext context)
{
    // Sample mock data: Dynamically change the mocked data for "actionName".
    return new CallExternalAPIActionMock(
        status: TestWorkflowStatus.Succeeded,
        outputs: new CallExternalAPIActionOutput {

            // If this account contains a JObject Body, 
            // set the properties you want here:
            // Body = "something".ToJObject()

        }
    );
}