C# : How to unit test Graph SDK Batch Request
Tags: csharp, graph, azure, unittest
Published: 2021-12-21 06:12:33

Even though writing unit tests for Azure Graph SDK became easier, writing unit test for Batch request was little bit confusing to me. 

# Source Code

This is my class which I want to write unit test. I omit some business logic on purpose, this is not simple wrapper in real project.

- DI ``GraphServiceClient``
- Return User objects via Batch Request
- Use ``AddBatchRequestStep`` to control requestId

```csharp
public class GraphService
{
    private readonly GraphServiceClient graphServiceClient;

    public GraphService(GraphServiceClient graphServiceClient)
    {
        this.graphServiceClient = graphServiceClient;
    }

    public async Task<List<User>> GetUsersAsync(List<string> userIds)
    {
        List<User> users = new();
        BatchRequestContent batchRequestContent = new();
        userIds.ForEach(x =>
        {
            batchRequestContent.AddBatchRequestStep(new BatchRequestStep(x, graphServiceClient.Users[x]
                    .Request().Select(u => new
                    {
                        u.Id,
                        u.GivenName,
                        u.Mail,
                        u.Surname,
                        u.UserPrincipalName
                    }).GetHttpRequestMessage()));
        });

        BatchResponseContent returnedResponse = await graphServiceClient.Batch.Request().PostAsync(batchRequestContent);
        userIds.ForEach(async x =>
        {
            User user = await returnedResponse.GetResponseByIdAsync<Microsoft.Graph.User>(x);
            users.Add(user);
        });

        return users;
    }
}
```

# Unit Test Code

I don't know if there is better way to achieve the same, but this is what I did by following advice in [GitHub issue](https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/1189)

- Create response as json string
- Mock ``IHttpProvider`` to handle ``SendAsync`` request. Return the json string as ``StringContent``
- Mock ``GraphServiceClient`` ahd pass mocked ``IHttpProvider`` as constructor
- Mock ``GetHttpRequestMessage`` method as this is called in ``AddBatchRequestStep``

```csharp
[Fact]
public async Task ReturnsUsers()
{
    // Arrenge
    string userId = "dummy_id1";
    string userId2 = "dummy_id2";
    string batchResponse = @$"{{
""responses"":[{{
    ""id"": ""{userId}"",
    ""status"":200,
    ""headers"":
    {{
        ""Content-Type"":""application/json""
    }},
    ""body"":
    {{
        ""@odata.context"":""https://graph.microsoft.com/v1.0/$metadata#users/$entity"",
        ""surName"":""Test"",
        ""givenName"":""User1"",
        ""id"":""{userId}""
    }}
  }},
{{
    ""id"": ""{userId2}"",
    ""status"":200,
    ""headers"":
    {{
        ""Content-Type"":""application/json""
    }},
    ""body"":
    {{
        ""@odata.context"":""https://graph.microsoft.com/v1.0/$metadata#users/$entity"",
        ""surName"":""Test"",
        ""givenName"":""User2"",
        ""id"":""{userId2}""
    }}
  }}]
}}";
    Mock<IHttpProvider> mockedIHttpProvider = new();
    mockedIHttpProvider.Setup(x => x.SendAsync(
        It.IsAny<HttpRequestMessage>(), 
        It.IsAny<HttpCompletionOption>(), 
        It.IsAny<CancellationToken>()))
        .ReturnsAsync(new HttpResponseMessage()
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent(batchResponse),
        });
    Mock<IAuthenticationProvider> mockedIAuthenticationProvider = new();
    Mock<GraphServiceClient> mockedGraphServiceClient = new(
        mockedIAuthenticationProvider.Object, mockedIHttpProvider.Object);
    mockedGraphServiceClient.Setup(x => x.Users[It.IsAny<string>()]
        .Request()
        .Select(It.IsAny<Expression<Func<Microsoft.Graph.User, object>>>()).GetHttpRequestMessage())
        .Returns(new HttpRequestMessage() { RequestUri = new Uri("http://localhost/v1.0/users") });
    GraphService graphService = new(mockedGraphServiceClient.Object);

    // Act
    List<User> users = await graphService.GetUsersAsync(new List<string>() { userId, userId2 }).ConfigureAwait(false);

    // Assert
    Assert.True(users.Count == 2);
}
```

# Summary

There are several trips required so I wish I can find better/easier way to achieve it in the future.

Of course we can mock any behavior and objects. Simply change request and response.