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
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
- Create response as json string
- Mock
IHttpProvider
to handleSendAsync
request. Return the json string asStringContent
- Mock
GraphServiceClient
ahd pass mockedIHttpProvider
as constructor - Mock
GetHttpRequestMessage
method as this is called inAddBatchRequestStep
[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.
Top comments (0)