Search code examples
c#azureunit-testingmicrosoft-graph-apiservicetestcase

How to write service test for UpdateAsync in Microsoft Graph API (Add multiple members to a group in a single request)


I am using Microsoft Graph Api client and performing add members to group.

Docs here :- https://learn.microsoft.com/en-us/graph/api/group-post-members?view=graph-rest-1.0&tabs=csharp#example-2-add-multiple-members-to-a-group-in-a-single-request

I have successfully achieved the requirement. But when come to write test for my service class having no clue what and how to verify it.

I am beginner to the API dev and also with Microsoft Graph API. Below are my codes, Take a look and post your suggestions and comments. It might be helpful.

Service Class:

public class UserGroupService : IUserGroupService
{
    private readonly IGraphServiceClient _graphServiceClient;

    public UserGroupService(IGraphServiceClient graphServiceClient)
    {
        _graphServiceClient = graphServiceClient;
    }
    
    public async Task AddAsync(string groupId, IList<string> userIds)
    {
        var group = new Group
        {
            AdditionalData = new Dictionary<string, object>()
            {
                {"[email protected]", userIds.Select(x => $"https://graph.microsoft.com/v1.0/directoryObjects/{x}") }
            }
        };

        await _graphServiceClient.Groups[groupId].Request().UpdateAsync(group);
    }
}

ServiceTest :

public class UserGroupServiceTests
    {
        private readonly Fixture _fixture = new Fixture();
        private readonly Mock<IGraphServiceClient> _graphServiceClientMock = new Mock<IGraphServiceClient>();
        private readonly IUserGroupService _userGroupService;

        public UserGroupServiceTests()
        {
            _userGroupService = new UserGroupService(_graphServiceClientMock.Object);
        }
        
        // Settingup GraphClientMock
        private void SetupGraphClientMock(string groupId, IList<string> userIds, Group group)
        {
            var groupRequest = new Mock<IGroupRequest>();

            var groupRequestBuilder = new Mock<IGroupRequestBuilder>();

            groupRequest.Setup(x => x.UpdateAsync(group));

            groupRequestBuilder.Setup(x => x.Request()).Returns(groupRequest.Object);

            _graphServiceClientMock.Setup(x => x.Groups[groupId]).Returns(groupRequestBuilder.Object);
        }
        
        [Fact]
        public async Task AddAsync_GivenValidInput_WhenServiceSuccessful_AddAsyncCalledOnce()
        {
            object result;
            var groupId = _fixture.Create<string>();
            var userIds = _fixture.Create<IList<string>>();
            var dictionary = _fixture.Create<Dictionary<string, object>>();
            dictionary.Add("[email protected]", userIds.Select(x => $"https://graph.microsoft.com/v1.0/directoryObjects/{x}"));
            var group = _fixture.Build<Group>().With(s => s.AdditionalData, dictionary).OmitAutoProperties().Create();

            SetupGraphClientMock(groupId, userIds, group);

            await _userGroupService.AddAsync(groupId, userIds);

            //TODO  
            // Need to verify _graphServiceClientMock AdditionalData value == mocking group AdditionalData value which is called once in _graphServiceClientMock.
            // Below implementation done using TryGetValue which return bool, I am really afraid to write test using bool value and compare and I feel its not a right way to write test.
            _graphServiceClientMock.Verify(m => m.Groups[groupId].Request().UpdateAsync(It.Is<Group>(x => x.AdditionalData.TryGetValue("[email protected]", out result) == group.AdditionalData.TryGetValue("[email protected]", out result))), Times.Once);
            _graphServiceClientMock.VerifyNoOtherCalls();
        }
    }

I wants to verify _graphServiceClientMock AdditionalData value == mocking group AdditionalData value which is called once in _graphServiceClientMock like the above. Anyone have idea on this. Please post your comments.Thanks in Advance.


Solution

  • Based on the subject under test and the simplicity of the provided member under test the following example demonstrates how it can be tested in isolation,

    public class UserGroupServiceTests {
    
        [Fact]
        public async Task AddAsync_GivenValidInput_WhenServiceSuccessful_AddAsyncCalledOnce() {
            //Arrange            
            string groupId = "123456";
            IList<string> userIds = new[] { "a", "b", "c" }.ToList();
            string expectedKey = "[email protected]";
            IEnumerable<string> expectedValues = userIds
                .Select(x => $"https://graph.microsoft.com/v1.0/directoryObjects/{x}");
            Group group = null;
    
            Mock<IGraphServiceClient> clientMock = new Mock<IGraphServiceClient>();
            clientMock
                .Setup(x => x.Groups[groupId].Request().UpdateAsync(It.IsAny<Group>()))
                .Callback((Group g) => group = g) //Capture passed group for assertion later
                .ReturnsAsync(group) //To allow async flow
                .Verifiable();
    
            IUserGroupService _userGroupService = new UserGroupService(clientMock.Object);
    
            //Act
            await _userGroupService.AddAsync(groupId, userIds);
    
            //Assert
            clientMock.Verify(); //have verifiable expressions been met
            clientMock.VerifyNoOtherCalls();
    
            //Using FluentAssertions to assert captured group
            group.Should().NotBeNull();//was a group passed
            group.AdditionalData.Should().NotBeNull()// did it have data
                .And.ContainKey(expectedKey);//and did the data have expected key
            (group.AdditionalData[expectedKey] as IEnumerable<string>)
                .Should().BeEquivalentTo(expectedValues);//are values as expected
        }
    }
    

    Review the code comments to get an understanding of how the test was exercised to verify the expected behavior.

    The intuitive nature of the used FluentAssertions should also help in understanding what is being asserted