Skip to content

Latest commit

 

History

History
179 lines (120 loc) · 7.58 KB

durable-functions-unit-testing.md

File metadata and controls

179 lines (120 loc) · 7.58 KB
title description services author manager keywords ms.service ms.devlang ms.topic ms.date ms.author
Azure Durable Functions unit testing
Learn how to unit test Durable Functions.
functions
kadimitr
jeconnoc
azure-functions
multiple
conceptual
02/28/2018
kadimitr

Durable Functions unit testing

Unit testing is an important part of modern software development practices. Unit tests verify business logic behavior and protect from introducing unnoticed breaking changes in the future. Durable Functions can easily grow in complexity so introducing unit tests will help to avoid breaking changes. The following sections explain how to unit test the three function types - Orchestration client, Orchestrator, and Activity functions.

Prerequisites

The examples in this article require knowledge of the following concepts and frameworks:

  • Unit testing

  • Durable Functions

  • xUnit - Testing framework

  • moq - Mocking framework

Base classes for mocking

Mocking is supported via two abstract classes in Durable Functions:

These classes are base classes for DurableOrchestrationClient and DurableOrchestrationContext that define Orchestration Client and Orchestrator methods. The mocks will set expected behavior for base class methods so the unit test can verify the business logic. There is a two-step workflow for unit testing the business logic in the Orchestration Client and Orchestrator:

  1. Use the base classes instead of the concrete implementation when defining Orchestration Client and Orchestrator's signatures.
  2. In the unit tests mock the behavior of the base classes and verify the business logic.

Find more details in the following paragraphs for testing functions that use the orchestration client binding and the orchestrator trigger binding.

Unit testing trigger functions

In this section, the unit test will validate the logic of the following HTTP trigger function for starting new orchestrations.

[!code-csharpMain]

The unit test task will be to verify the value of the Retry-After header provided in the response payload. So the unit test will mock some of DurableOrchestrationClientBase methods to ensure predictable behavior.

First, a mock of the base class is required, DurableOrchestrationClientBase. The mock can be a new class that implements DurableOrchestrationClientBase. However, using a mocking framework like moq simplifies the process:

    // Mock DurableOrchestrationClientBase
    var durableOrchestrationClientBaseMock = new Mock<DurableOrchestrationClientBase>();

Then StartNewAsync method is mocked to return a well-known instance ID.

    // Mock StartNewAsync method
    durableOrchestrationClientBaseMock.
        Setup(x => x.StartNewAsync(functionName, It.IsAny<object>())).
        ReturnsAsync(instanceId);

Next CreateCheckStatusResponse is mocked to always return an empty HTTP 200 response.

    // Mock CreateCheckStatusResponse method
    durableOrchestrationClientBaseMock
        .Setup(x => x.CreateCheckStatusResponse(It.IsAny<HttpRequestMessage>(), instanceId))
        .Returns(new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent(string.Empty),
        });

TraceWriter is also mocked:

    // Mock TraceWriter
    var traceWriterMock = new Mock<TraceWriter>(TraceLevel.Info);

Now the Run method is called from the unit test:

    // Call Orchestration trigger function
    var result = await HttpStart.Run(
        new HttpRequestMessage()
        {
            Content = new StringContent("{}", Encoding.UTF8, "application/json"),
            RequestUri = new Uri("http://localhost:7071/orchestrators/E1_HelloSequence"),
        },
        durableOrchestrationClientBaseMock.Object, 
        functionName,
        traceWriterMock.Object);

The last step is to compare the output with the expected value:

    // Validate that output is not null
    Assert.NotNull(result.Headers.RetryAfter);

    // Validate output's Retry-After header value
    Assert.Equal(TimeSpan.FromSeconds(10), result.Headers.RetryAfter.Delta);

After combining all steps, the unit test will have the following code:

[!code-csharpMain]

Unit testing orchestrator functions

Orchestrator functions are even more interesting for unit testing since they usually have a lot more business logic.

In this section the unit tests will validate the output of the E1_HelloSequence Orchestrator function:

[!code-csharpMain]

The unit test code will start with creating a mock:

    var durableOrchestrationContextMock = new Mock<DurableOrchestrationContextBase>();

Then the activity method calls will be mocked:

    durableOrchestrationContextMock.Setup(x => x.CallActivityAsync<string>("E1_SayHello", "Tokyo")).ReturnsAsync("Hello Tokyo!");
    durableOrchestrationContextMock.Setup(x => x.CallActivityAsync<string>("E1_SayHello", "Seattle")).ReturnsAsync("Hello Seattle!");
    durableOrchestrationContextMock.Setup(x => x.CallActivityAsync<string>("E1_SayHello", "London")).ReturnsAsync("Hello London!");

Next the unit test will call HelloSequence.Run method:

    var result = await HelloSequence.Run(durableOrchestrationContextMock.Object);

And finally the output will be validated:

    Assert.Equal(3, result.Count);
    Assert.Equal("Hello Tokyo!", result[0]);
    Assert.Equal("Hello Seattle!", result[1]);
    Assert.Equal("Hello London!", result[2]);

After combining all steps, the unit test will have the following code:

[!code-csharpMain]

Unit testing activity functions

Activity functions can be unit tested in the same way as non-durable functions. Activity functions don't have a base class for mocking. So the unit tests use the parameter types directly.

In this section the unit test will validate the behavior of the E1_SayHello Activity function:

[!code-csharpMain]

And the unit test will verify the format of the output:

[!code-csharpMain]

Next steps

[!div class="nextstepaction"] Learn more about xUnit

Learn more about moq