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)