Search code examples
c#unit-testingmoqresource-management

How to mock ResourceGroupCollection using Moq?


My code is the following:

SubscriptionResource subscription = await armClient.GetDefaultSubscriptionAsync();
var resourceGroup = subscription.GetResourceGroups().FirstOrDefault(rg => rg.Data.Name.Equals(resourceGroupName));

GetResourceGroups() returns an instance of ResourceGroupCollection. When the ResourceGroupCollection is enumerated by SingleOrDefault, I'm trying to get the mock to return a mock instance of a ResourceGroupResource. However, I'm not sure how to do that.

So far I have this code in my Moq test:

this.resourceGroupCollectionMock = new Mock<ResourceGroupCollection>() { CallBase = true };

Mock<ResourceGroupResource> adfResource = new Mock<ResourceGroupResource>();
ResourceGroupData rgData = ResourceManagerModelFactory.ResourceGroupData(resourceIdentifier, resourceGroupName);
adfResource.Setup(x => x.Data).Returns(rgData);

this.subscriptionResourceMock.Setup(x => x.GetResourceGroups()).Returns(this.resourceGroupCollectionMock.Object);

As you can see in my setup, I mocked GetResourceGroups() to return the mock ResourceGroupCollection object. But I'm not sure how to add adfResource to that mock resourceGroupCollectionMock, so that it is returned when the latter is enumerated.


Solution

  • TL;DR: Unfortunately you can't mock ResourceGroupData properly because ResourceData's Name (1, 2) is not defined as virtual/abstract ;(

    //Arrange
    const string resourceGroupName = "A";
    
    Mock<ResourceGroupData> data = new();
    // data
    //     .SetupGet(x => x.Name)
    //     .Returns(resourceGroupName); 
    
    // The above commented mock setup would throw NotSupportedException
    
    Mock<ResourceGroupResource> resource = new();
    resource
        .SetupGet(x => x.Data)
        .Returns(data.Object);
    
    Mock<Response> response = new();
    var pagedResource = Page<ResourceGroupResource>.FromValues(new ResourceGroupResource[] { resource.Object }, null, response.Object);
    var pagedResources = Pageable<ResourceGroupResource>.FromPages(new Page<ResourceGroupResource>[] { pagedResource });
    
    Mock<ResourceGroupCollection> collection = new();
    collection
        .Setup(x => x.GetAll(It.IsAny<string>(), It.IsAny<int?>(), It.IsAny<CancellationToken>()))
        .Returns(pagedResources);
    
    Mock<SubscriptionResource> subscription = new();    
    subscription
        .Setup(x => x.GetResourceGroups())
        .Returns(collection.Object);
    
    //Act
    var actual = subscription.Object.GetResourceGroups()
        .FirstOrDefault();
        //.FirstOrDefault(rg => rg.Data.Name.Equals(resourceGroupName)); 
    
    // The above commented line would throw NullReferenceException
    
    //Assert
    Assert.NotNull(actual);
    

    So, if you don't use the Name property in your predicate (like in the above sample) then you can mock/fake the rest of the classes.