Skip to content

Using the Testing Framework

Mark Abrams edited this page Jun 5, 2023 · 6 revisions

The WorkflowTestBase class is an abstract base class that contains functionality to set up and configure the testing framework. All test classes should inherit from this base class. This example uses test attributes for MSTest to define a test class and the test initialization and clean-up methods:

[TestClass]
public class MyWorkflowTest : WorkflowTestBase
{
    [TestInitialize]
    public void TestInitialize()
    {
        Initialize("../../../../LogicAppUnit.Samples.LogicApps", "my-test-workflow");
    }

    [ClassCleanup]
    public static void CleanResources()
    {
        Close();
    }
}

The Initialize() method is used to configure a workflow for testing. The path to the Logic App's root folder and the name of the workflow are passed as parameters. The actions performed by this method to prepare the workflow for testing are described in later sections.

The Close() method is used to free up the resources used by the testing framework, once all tests in the test class have completed.

Setting up a Test

A workflow test is executed using an implementation of ITestRunner. This is created using the CreateTestRunner() method from the base class:

[TestMethod]
public void WorkflowTest()
{
    using (ITestRunner testRunner = CreateTestRunner())
    {

An instance of ITestRunner should only be used for a single test.

The next step is to configure the responses for the requests that are sent to the mock HTTP server, using the TestRunner.AddApiMocks() property. This example mocks the responses for workflow actions that connect to SQL Server and Service Bus:

// Mock the SQL and Service Bus actions and customize responses
// For both types of actions, the URI in the request matches the action name
testRunner.AddApiMocks = (request) =>
{
    HttpResponseMessage mockedResponse = new HttpResponseMessage();
    if (request.RequestUri.AbsolutePath == "/Execute_Query_to_get_Language_Name")
    {
        mockedResponse.RequestMessage = request;
        mockedResponse.StatusCode = HttpStatusCode.OK;
        mockedResponse.Content = ContentHelper.CreateJsonStringContent(GetSqlExecuteResponseContent());
    }
    else if (request.RequestUri.AbsolutePath == "/Send_message_to_Topic")
    {
        // No response content for Service Bus actions
        mockedResponse.RequestMessage = request;
        mockedResponse.StatusCode = HttpStatusCode.OK;
    }
    return mockedResponse;
};

The ContentHelper class is part of the testing framework and contains methods that are useful when creating HTTP content (JSON, XML and plain text) for the mocked responses.

Running a Test

The next step is to run the workflow. The TestRunner.TriggerWorkflow() method creates a HTTP request for the workflow trigger and sends it to the workflow. This example uses HTTP POST but other HTTP methods can be used:

HttpResponseMessage workflowResponse = testRunner.TriggerWorkflow(FunctionToGetTriggerContent(), HttpMethod.Post);

The TriggerWorkflow() method will complete when the workflow execution has completed.

There are a few overloads of the TriggerWorkflow() method that allow you to set the following:

  • HTTP request headers.
  • URL query parameters.
  • The relative path, if the HTTP trigger is configured to use a relative path. The relative path must be URL-encoded by the test case, this is not done by the test runner.

The trigger URL for the workflow is logged to the test execution log. This example is for a workflow that uses a relative path of /thisIsMyContainer/thisIsMyBlob:

Workflow trigger:
    Name: manual
    URL: POST http://localhost:7071/api/stateless-test-workflow/triggers/manual/invoke/thisIsMyContainer/thisIsMyBlob?api-version=2022-05-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=123ao4DL03l1c1C-KRqfsl9hr0G_ipOjg_h77STbAWQ

The example shows a trigger with the name of manual, but the testing framework makes no assumptions about the name of the trigger, it can be set to any valid value.

Test Behaviour when Triggering a Workflow

The behaviour of the TestRunner.TriggerWorkflow() method, and the response from the method, depends on the workflow definition and the configuration of Asynchronous Response Processing, as follows:

Workflow Scenario Async Response Processing enabled? TriggerWorkflow() response Notes
HTTP Trigger and a synchronous Response action n/a The response created by the synchronous Response action
HTTP Trigger and an asynchronous Response action yes The response created by the asynchronous Response action The testing framework automatically receives the synchronous HTTP 202 (Accepted) response from the HTTP trigger and uses the callback URL (as defined in the Location header) to poll the workflow for the asynchronous response. It is this asynchronous response that is returned.
HTTP Trigger and an asynchronous Response action no The automatic response created by the HTTP trigger The testing framework automatically receives the synchronous HTTP 202 (Accepted) response from the HTTP trigger and it is this response that is returned. The callback is ignored.
HTTP Trigger and no Response action n/a The automatic response created by the HTTP trigger The testing framework receives a synchronous HTTP 202 (Accepted) response from the HTTP trigger and it is this response that is returned.
A non-HTTP trigger n/a The automatic response created by the HTTP trigger that replaces the non-HTTP trigger The testing framework replaces all non-HTTP triggers with HTTP triggers, but a matching Response action is not added to the workflow definition.

As above, the framework receives a synchronous HTTP 202 (Accepted) response from the HTTP trigger and it is this response that is returned.

In all scenarios the TriggerWorkflow() method will complete when the workflow execution has completed. If the Response action is followed by other actions, the TriggerWorkflow() method will complete only when the other actions have completed (or been skipped).

Enabling Asynchronous Response Processing

Asynchronous Response Processing for a test case is disabled by default. It is enabled by calling the TestRunner.WaitForAsynchronousResponse() method:

using (ITestRunner testRunner = CreateTestRunner())
{
    // Configure async response handling in seconds
    testRunner.WaitForAsynchronousResponse(30);

The parameter indicates how many seconds the testing framework should wait for the asynchronous response to be generated by the workflow. A method overload allows the wait period to be expressed as a TimeSpan:

using (ITestRunner testRunner = CreateTestRunner())
{
    // Configure async response handling as a TimeSpan
    testRunner.WaitForAsynchronousResponse(new TimeSpan(0, 2, 0);

If Asynchronous Response Processing is enabled, the workflow callback URL is logged to the test execution log:

Workflow async callback:
    URL: GET http://localhost:7071/runtime/webhooks/workflow/scaleUnits/prod-00/workflows/bc2509cb08ce4d58b04105ef4ab7e178/runs/08585159864168441072386309873CU00/operations/4ac870fc-b315-4f68-a047-5c4a54ab7464?api-version=2022-05-01&sp=%2Fruns%2F08585159864168441072386309873CU00%2Foperations%2F4ac870fc-b315-4f68-a047-5c4a54ab7464%2Fread&sv=1.0&sig=nmRM8486HP5vmGFjKUMh4HQucVMzDTQRPk62
    Timeout for receipt of async response: 30 seconds
Clone this wiki locally