Question:
How can I get the original type of a Mock after the Mock has been cast to an object


Problem:

I have a ServiceCollection, and I want to make a method where I can pass in a set of parameters, and have the ServiceCollection return those services when the matching type is requested. I am using >Autofixture to create my services. When I call fixture.Create<IMyService>, it'll return a Mock<IMyService>. For example

public static IServiceProvider CreateServiceProvider(params object[] services)

{

    var serviceCollection = new ServiceCollection();

    

    foreach (object service in services)

    {

       serviceCollection.AddTransient(factory => service);

    }


    return serviceCollection.BuildServiceProvider();

}


And then I want to use it like this



[Fact]

public void TestSomething()

{

   // This causes myFirstService to be of type of type IMyFirstService,

   // but have a value of type Mock<IMyFirstService> (same for

   // mySecondService)

   var fixture = new Fixture();

   IMyFirstService myFirstService = fixture.Create<IMyFirstService>();

   IMySecondService mySecondService = fixture.Create<IMySecondService>();


   var serviceProvider = CreateServiceProvider(myFirstService, mySecondService);


   // I want the returned objects to be myFirstService and mySecondService

   // that I created above

   var resolvedFirstService = serviceProvider.GetService<IMyFirstService>();

   var resolvedSecondService = serviceProvider.GetService<IMySecondService>();

}



The problem is that myFirstService and mySecondService are not returned, because the services are cast to an object when CreateServiceProvider is called, so they get registered with the ServiceCollection as type object instead of the correct interface type. I tried using serviceProvider.GetService(service.GetType(), factory => service) but the problem is that service.GetType() returns Castle.Proxies.IMyFirstServiceProxy.


I know I can make this work by doing the code below instead, but I want it to work by having the CreateServiceProvider method register those services.



[Fact]

public void TestSomething()

{

   var fixture = new Fixture();

   IMyFirstService myFirstService = fixture.Create<IMyFirstService>();

   IMySecondService mySecondService = fixture.Create<IMySecondService>();


   var serviceCollection = new ServiceCollection();

   serviceCollection.AddTransient(factory => myFirstService);

   serviceCollection.AddTransient(factory => mySecondService);

   var serviceProvider = serviceCollection.BuildServiceProvider();


   var resolvedFirstService = serviceProvider.GetService<IMyFirstService>();

   var resolvedSecondService = serviceProvider.GetService<IMySecondService>();

}



Solution:

I do not know an easy way to achieve that but if you are adamant on going this way you can go with quite brittle path of the reflection and messing with implementation details:

static IServiceProvider CreateServiceProvider(params object[] services)

{

    var serviceCollection = new ServiceCollection();


    foreach (object service in services)

    {

        // relying on the mocked object implementing IMocked:

        var mocked = service as IMocked; 


        // using internal property:

        var mockedType = typeof(Mock).GetProperty("MockedType", BindingFlags.Instance | BindingFlags.NonPublic)

            .GetValue(mocked.Mock) as Type;


        serviceCollection.AddTransient(mockedType, _ => service);

    }


    return serviceCollection.BuildServiceProvider();

}



But again this can be quite brittle so I recommend to reconsider the approach and use Type's as parameters:

static IServiceProvider CreateServiceProvider(params Type[] services)

{

    var serviceCollection = new ServiceCollection();

    var fixture = new Fixture().Customize(new AutoMoqCustomization());

    var context = new SpecimenContext(fixture);

    foreach (var serviceType in services)

    {

        var instance = fixture.Create(serviceType, context);

        serviceCollection.AddTransient(serviceType, _ => instance);

    }


    return serviceCollection.BuildServiceProvider();

}



With call like:

var serviceProvider = CreateServiceProvider(typeof(IMyFirstService), typeof(IMySecondService));


Suggested blogs:

>What is data binding in Angular?

>What is microservice architecture, and why is it better than monolithic architecture?

>What is pipe in Angular?

>What makes Python 'flow' with HTML nicely as compared to PHP?

>What to do when MQTT terminates an infinite loop while subscribing to a topic in Python?


Ritu Singh

Ritu Singh

Submit
0 Answers