|
| 1 | +--- |
| 2 | +title: Create Unit Tests from Standard Workflows with Visual Studio Code |
| 3 | +description: Create unit tests from Standard workflow definitions in Azure Logic Apps by using Visual Studio Code. |
| 4 | +services: logic-apps |
| 5 | +ms.suite: integration |
| 6 | +author: wsilveiranz |
| 7 | +ms.author: wsilveira |
| 8 | +ms.reviewer: estfan, azla |
| 9 | +ms.topic: how-to |
| 10 | +ms.date: 04/14/2025 |
| 11 | +# Customer intent: As logic app developer, I want to learn how to create a unit test that mocks the operations and outputs from a Standard workflow definition in Azure Logic Apps with Visual Studio Code. |
| 12 | +--- |
| 13 | + |
| 14 | +# Create unit tests from Standard workflow definitions in Azure Logic Apps with Visual Studio Code (Preview) |
| 15 | + |
| 16 | +> [!NOTE] |
| 17 | +> |
| 18 | +> This capability is in preview and is subject to the [**Supplemental Terms of Use for Microsoft Azure Previews**](https://azure.microsoft.com/support/legal/preview-supplemental-terms/). |
| 19 | +
|
| 20 | +Unit testing is an essential practice that keeps your app or solution reliable and accurate throughout the software development lifecycle. Unit tests help you efficiently and systematically validate the key components in your solution. |
| 21 | + |
| 22 | +For Standard logic app workflows, you can create unit tests by using Visual Studio Code and the Azure Logic Apps (Standard) extension. This capability lets you use workflow definitions to create unit tests and tailor them to scenarios supported by your logic app solution - all without needing connections to any external services, systems, or APIs. This approach lets you test your workflows without having to interact with external services, systems, or APIs and provides the following benefits: |
| 23 | + |
| 24 | +- Improve workflow quality by identifying and addressing potential issues before you deploy to other environments. |
| 25 | + |
| 26 | +- Streamline unit test integration with your development process, while ensuring consistent and accurate workflow behavior. |
| 27 | + |
| 28 | +This guide shows how to create a unit test definition from a workflow. This definition mocks the external calls from each workflow operation without changing the workflow logic. When you create a unit test for a workflow, you get a unit test project that includes the following folders: |
| 29 | + |
| 30 | +- A folder that contains strongly typed classes for each mockable operation in your workflow. |
| 31 | + |
| 32 | +- A folder for each unit test definition. This folder includes a C# file that contains a sample class and methods. You use this class and methods to set up your own assertions, confirm that the workflow behaves as expected, and make sure that the workflow behaves reliably and predictably in your larger Azure ecosystem. |
| 33 | + |
| 34 | +## Prerequisites |
| 35 | + |
| 36 | +- An Azure account and subscription. If you don't have a subscription, [sign up for a free Azure account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). |
| 37 | + |
| 38 | +- A Standard logic app project in Visual Studio Code that contains at least one workflow definition to use for creating a unit test. |
| 39 | + |
| 40 | + For more information about Visual Studio Code setup and project creation, see [Create Standard logic app workflows with Visual Studio Code](/azure/logic-apps/create-standard-workflows-visual-studio-code). |
| 41 | + |
| 42 | +## Limitations and known issues |
| 43 | + |
| 44 | +- This release currently supports only C# for creating unit tests. |
| 45 | + |
| 46 | +- This release doesn't support non-mocked actions. Make sure that all actions in the workflow execution path are mocked. |
| 47 | + |
| 48 | +- This release doesn't support the following action types: |
| 49 | + |
| 50 | + - Integration account actions |
| 51 | + - Data Mapper actions |
| 52 | + - Custom code actions |
| 53 | + - XML actions |
| 54 | + - Liquid actions |
| 55 | + - EDI encode and decode actions |
| 56 | + |
| 57 | +## Review the basic concepts |
| 58 | + |
| 59 | +The following list includes basic but important concepts about unit tests for Standard workflows: |
| 60 | + |
| 61 | +- **Logic app unit test** |
| 62 | + |
| 63 | + A controlled workflow execution that injects mock objects. These objects represent either the workflow trigger or actions that depend on external services or systems. |
| 64 | + |
| 65 | +- **Mockable action** |
| 66 | + |
| 67 | + A workflow action that depends on an external service or system. You can convert these actions to mocked actions for unit test creation and execution. |
| 68 | + |
| 69 | +## Create a unit test from a workflow definition |
| 70 | + |
| 71 | +1. In Visual Studio Code, open your Standard logic app project. |
| 72 | + |
| 73 | +1. In your project, expand the workflow definition folder. |
| 74 | + |
| 75 | +1. From the shortcut menu for the **workflow.json** file, select **Open Designer**. |
| 76 | + |
| 77 | +1. On the designer toolbar, select **Create unit test**. |
| 78 | + |
| 79 | + :::image type="content" source="media/create-unit-tests-standard-workflow-definitions-visual-studio-code/designer.png" alt-text="Screenshot shows Visual Studio Code, Standard logic app project, workflow designer, and selected command to create unit test." lightbox="media/create-unit-tests-standard-workflow-definitions-visual-studio-code/designer.png"::: |
| 80 | + |
| 81 | +1. Provide a name to use for the unit test, unit test class, and C# file. |
| 82 | + |
| 83 | + A new folder named **Tests** now appears in your project workspace. This folder has the following structure: |
| 84 | + |
| 85 | + :::image type="content" source="media/create-unit-tests-standard-workflow-definitions-visual-studio-code/unit-test-project-structure.png" alt-text="Screenshot shows Visual Studio Code, Standard logic app project, and Tests folder with unit test folders and files." lightbox="media/create-unit-tests-standard-workflow-definitions-visual-studio-code/unit-test-project-structure.png"::: |
| 86 | + |
| 87 | + | Folder or file | Description | |
| 88 | + |----------------|-------------| |
| 89 | + | **`Tests`** <br>**\|\| <`logic-app-name`>** | In the **`Tests`** folder, a **<`logic-app-name`>** folder appears when you add unit tests to a logic app project. | |
| 90 | + | **`Tests`** <br>**\|\| <`logic-app-name`>** <br>**\|\|\| <`workflow-name`>** | In the **<`logic-app-name`>** folder, a **<`workflow-name`>** folder appears when you add unit tests for a workflow. | |
| 91 | + | **`Tests`** <br>**\|\| <`logic-app-name`>** <br>**\|\|\| <`workflow-name`>** <br>**\|\|\|\| `MockOutputs`** <br>**\|\|\|\|\| <`operation-name-outputs`>**.**`cs`** | In the **<`workflow-name`>** folder, the **`MockOutputs`** folder contains a C# (**.cs**) file with strongly-typed classes for each connector operation in the workflow. Each **.cs** file name uses the following format: <br><br>**<`operation-name`>[`Trigger\|Action`]`Output.cs`** <br><br>If a connector operation has *dynamic contracts*, a class appears for each *dynamic type*. A dynamic type refers to an operation parameter that has different inputs and outputs based on the value provided for that parameter. You can use these classes to extend your unit tests and create new mocks from scratch. | |
| 92 | + | **`Tests`** <br>**\|\| <`logic-app-name`>** <br>**\|\|\| <`workflow-name`>** <br>**\|\|\|\| <`unit-test-name`>** <br>**\|\|\|\|\| <`unit-test-name`>`.cs`** | In the **<`workflow-name`>** folder, the **<`unit-test-name`>** folder contains a **<`unit-test-name`>`.cs`** file. You use this file, which contains a sample C# class and methods, to run and assert results. You can edit this file to match your specific test scenarios. | |
| 93 | + |
| 94 | +## Review the unit test *.cs file |
| 95 | + |
| 96 | +This unit test class provides a framework for testing Standard logic app workflows by mocking triggers and actions. This class lets you test workflows without actually calling external services or APIs. |
| 97 | + |
| 98 | +### Test class structure |
| 99 | + |
| 100 | +A typical unit test class uses the following structure: |
| 101 | + |
| 102 | +```csharp |
| 103 | +[TestClass] |
| 104 | +public class <unit-test-name> |
| 105 | +{ |
| 106 | + public TestExecutor TestExecutor; |
| 107 | + |
| 108 | + [TestInitialize] |
| 109 | + public void Setup() |
| 110 | + { |
| 111 | + this.TestExecutor = new TestExecutor("<workflow-name>/testSettings.config"); |
| 112 | + } |
| 113 | + |
| 114 | + // Add test methods here. |
| 115 | +
|
| 116 | + // Add helper methods here. |
| 117 | +} |
| 118 | +``` |
| 119 | + |
| 120 | +#### Setup() method |
| 121 | + |
| 122 | +This method instantiates the **`TestExecutor`** class by using the path to your test settings configuration file. The method runs before each test execution and creates a new instance of **`TestExecutor`**. |
| 123 | + |
| 124 | +```csharp |
| 125 | +[TestInitialize] |
| 126 | +public void Setup() |
| 127 | +{ |
| 128 | + this.TestExecutor = new TestExecutor("<workflow-name>/testSettings.config"); |
| 129 | +} |
| 130 | +``` |
| 131 | + |
| 132 | +### Sample test methods |
| 133 | + |
| 134 | +The following section describes sample test methods that you can use in your unit test class. |
| 135 | + |
| 136 | +#### Static mock data test |
| 137 | + |
| 138 | +The following method shows how to use static mock data to test your workflow. In this method, you can complete the following tasks: |
| 139 | + |
| 140 | +- Set property values on your mocked actions. |
| 141 | +- Execute the workflow with the configured mock data. |
| 142 | +- Confirm that the execution succeeded. |
| 143 | + |
| 144 | +```csharp |
| 145 | +[TestMethod] |
| 146 | +public async Task <workflow-name>_<unit-test-name>_ExecuteWorkflow_SUCCESS_Sample1() |
| 147 | +{ |
| 148 | + // PREPARE mock: Generate mock trigger data. |
| 149 | + var triggerMockOutput = new WhenMessagesAreAvailableInAQueuePeeklockTriggerOutput(); |
| 150 | + // Sample that shows how to set the properties for triggerMockOutput |
| 151 | + // triggerMockOutput.Body.Id = "SampleId"; |
| 152 | + var triggerMock = new WhenMessagesAreAvailableInAQueuePeeklockTriggerMock(outputs: triggerMockOutput); |
| 153 | + |
| 154 | + // Generate mock action data. |
| 155 | + var actionMockOutput = new CallExternalAPIActionOutput(); |
| 156 | + // Sample that shows how to set the properties for actionMockOutput |
| 157 | + // actionMockOutput.Body.Name = "SampleResource"; |
| 158 | + // actionMockOutput.Body.Id = "SampleId"; |
| 159 | + var actionMock = new CallExternalAPIActionMock(name: "Call_External_API", outputs: actionMockOutput); |
| 160 | + |
| 161 | + // ACT: Create the UnitTestExecutor instance. Run the workflow with mock data. |
| 162 | + var testMock = new TestMockDefinition( |
| 163 | + triggerMock: triggerMock, |
| 164 | + actionMocks: new Dictionary<string, ActionMock>() |
| 165 | + { |
| 166 | + {actionMock.Name, actionMock} |
| 167 | + }); |
| 168 | + var testRun = await this.TestExecutor |
| 169 | + .Create() |
| 170 | + .RunWorkflowAsync(testMock: testMock).ConfigureAwait(continueOnCapturedContext: false); |
| 171 | + |
| 172 | + // ASSERT: Confirm successful workflow execution and that the status is 'Succeeded'. |
| 173 | + Assert.IsNotNull(value: testRun); |
| 174 | + Assert.AreEqual(expected: TestWorkflowStatus.Succeeded, actual: te |
| 175 | + stRun.Status); |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +#### Dynamic mock data test |
| 180 | + |
| 181 | +The following method shows how to use dynamic mock data with callback methods. This approach gives you two options that dynamically generate mock data: |
| 182 | + |
| 183 | +- Define a separate callback method. |
| 184 | +- Use an [inline lambda function](/dotnet/csharp/language-reference/operators/lambda-expressions). |
| 185 | + |
| 186 | +Both approaches let you create dynamic responses based on unit test execution context. |
| 187 | + |
| 188 | +```csharp |
| 189 | +[TestMethod] |
| 190 | +public async Task <workflow-name>_<unit-test-name>_ExecuteWorkflow_SUCCESS_Sample2() |
| 191 | +{ |
| 192 | + // PREPARE: Generate mock trigger data. |
| 193 | + var triggerMockOutput = new WhenMessagesAreAvailableInAQueuePeeklockTriggerOutput(); |
| 194 | + // Sample that shows how to set triggerMockOutput properties. |
| 195 | + // triggerMockOutput.Body.Flag = true; |
| 196 | + var triggerMock = new WhenMessagesAreAvailableInAQueuePeeklockTriggerMock(outputs: triggerMockOutput); |
| 197 | + |
| 198 | + // PREPARE: Generate mock action data. |
| 199 | + // OPTION 1: Define a callback class. |
| 200 | + var actionMock = new CallExternalAPIActionMock(name: "Call_External_API", onGetActionMock: CallExternalAPIActionMockOutputCallback); |
| 201 | + |
| 202 | + // OPTION 2: Define inline with a lambda function. |
| 203 | + /*var actionMock = new CallExternalAPIActionMock(name: "Call_External_API", onGetActionMock: (testExecutionContext) => |
| 204 | + { |
| 205 | + return new CallExternalAPIActionMock( |
| 206 | + status: TestWorkflowStatus.Succeeded, |
| 207 | + outputs: new CallExternalAPIActionOutput { |
| 208 | +
|
| 209 | + // If this account contains a JObject Body, |
| 210 | + // set the properties you want here: |
| 211 | + // Body = "something".ToJObject() |
| 212 | +
|
| 213 | + } |
| 214 | + ); |
| 215 | + });*/ |
| 216 | + |
| 217 | + // ACT: Create the UnitTestExecutor instance. Run the workflow with mock data. |
| 218 | + var testMock = new TestMockDefinition( |
| 219 | + triggerMock: triggerMock, |
| 220 | + actionMocks: new Dictionary<string, ActionMock>() |
| 221 | + { |
| 222 | + {actionMock.Name, actionMock} |
| 223 | + }); |
| 224 | + var testRun = await this.TestExecutor |
| 225 | + .Create() |
| 226 | + .RunWorkflowAsync(testMock: testMock).ConfigureAwait(continueOnCapturedContext: false); |
| 227 | + |
| 228 | + // ASSERT: Confirm successful workflow execution and that the status is 'Succeeded'. |
| 229 | + Assert.IsNotNull(value: testRun); |
| 230 | + Assert.AreEqual(expected: TestWorkflowStatus.Succeeded, actual: testRun.Status); |
| 231 | +} |
| 232 | +``` |
| 233 | + |
| 234 | +### Helper methods |
| 235 | + |
| 236 | +The following section describes methods used by the sample test methods. Helper methods appear under the test methods in the class definition. |
| 237 | + |
| 238 | +#### Callback method |
| 239 | + |
| 240 | +The following method dynamically generates mock data. The method name varies based on the mocked action name in the test methods for static or dynamic mock data. You can edit this method to return different mock responses based on your test scenario requirements or use it as a template to create your own dynamic callback methods. |
| 241 | + |
| 242 | +```csharp |
| 243 | +public CallExternalAPIActionMock CallExternalAPIActionMockOutputCallback(TestExecutionContext context) |
| 244 | +{ |
| 245 | + // Sample mock data: Dynamically change the mocked data for 'actionName'. |
| 246 | + return new CallExternalAPIActionMock( |
| 247 | + status: TestWorkflowStatus.Succeeded, |
| 248 | + outputs: new CallExternalAPIActionOutput { |
| 249 | + |
| 250 | + // If this account contains a JObject Body, |
| 251 | + // set the properties you want here: |
| 252 | + // Body = "something".ToJObject() |
| 253 | +
|
| 254 | + } |
| 255 | + ); |
| 256 | +} |
| 257 | +``` |
| 258 | + |
| 259 | +## Related content |
| 260 | + |
| 261 | +[Create unit tests from Standard workflow runs in Azure Logic Apps with Visual Studio Code](/azure/logic-apps/create-unit-tests-standard-workflow-runs-visual-studio-codes) |
0 commit comments