Search code examples
unit-testingtestingmoqazure-service-fabric

Testing FabricClient object


I need to test a method that depends on the object System.Fabric.FabricClient. Specifically, the method reads a list of Partitions from this object:

ServicePartitionList partitions = await this.fabricClient.QueryManager.GetPartitionListAsync(serviceName);

Then, the method goes through the list and obtains data for each partition.

foreach (Partition partition in partitions)
{
    // ... code to verify
    LowKey = ((Int64RangePartitionInformation)partition.PartitionInformation).LowKey;
    PartitionId = partition.PartitionInformation.Id;
    Health = partition.HealthState;
    // ... code to verify
}

I'm using moq library for mock interfaces but I can't use it with this concrete class.

I've tried wrapping the FabricClient with an interface but all the objects implicated are abstract or sealed and I have been unable to instantiate these objects.

I also tried to use ServiceFabric.Mocks but I had no luck. I think FabricClient needs the service running to work.

As a summary, what I need is that the FabricClient object does not throw me any errors when I try to read the partition list and, if possible, get fake values from it.

Edit:

Finally, I've wrapped all the objects I needed from the FabricClient object and exposed them through an interface:

public interface IFabricClientWrapper
{
    Task<ServicePartitionListWrapper> GetPartitionListAsync(Uri serviceName);
}

public class FabricClientWrapper : IFabricClientWrapper
{
    private FabricClient fabricClient;

    public FabricClientWrapper()
    {
        fabricClient = new FabricClient();
    }

    public async Task<ServicePartitionListWrapper> GetPartitionListAsync(Uri serviceName)
    {
        var list = new ServicePartitionListWrapper();
        var partitionList = await fabricClient.QueryManager.GetPartitionListAsync(serviceName);
        foreach (var partition in partitionList)
        {
            PartitionWrapper partitionWrapper = new PartitionWrapper();
            partitionWrapper.Id = partition.PartitionInformation.Id;
            partitionWrapper.HealthState = partition.HealthState;
            partitionWrapper.LowKey = ((Int64RangePartitionInformation)partition.PartitionInformation).LowKey;
            partitionWrapper.HighKey = ((Int64RangePartitionInformation)partition.PartitionInformation).HighKey;
            list.Add(partitionWrapper);
        }
        return list;
    }
}

public class ServicePartitionListWrapper : List<PartitionWrapper> { }

public class PartitionWrapper
{
    public Guid Id { get; set; }
    public HealthState HealthState { get; set; }
    public long LowKey { get; set; }
    public long HighKey { get; set; }
}

Solution

  • FabricClient is tightly coupled to the cluster runtime.

    You could use a layer of abstraction here. Instead of calling the QueryManager directly, wrap it inside another class similar to this:

    public class PartitionInfoProvider : IPartitionInfoProvider 
    {
       private FabricClient fabricClient;
    
       public PartitionInfoProvider(FabricClient fabricClient)
       {
          this.fabricClient = fabricClient;
       }
    
       public Task<ServicePartitionList> GetPartitions()
       {
          return this.fabricClient.QueryManager.GetPartitionListAsync(serviceName);          
       }
    }
    

    Pass the implementation to the Service constructor, using the interface declaration.

    You can then mock provide a mock implementation of this provider, when running your test.