How to mock Autofaq interface with Moq, when an extension method is called

Viewed 3508

Using C#/Autofac/Moq: I have this class:

public class MyService
{
    private readonly IlifetimeScope _scope;

    public MyService(ILifetimeScope scope)
    {
        _scope = scope;
    }

    public void DoWork()
    {
         var dbContext = _scope.Resolve<IDataContext>();
    }
}

This works at runtime. However, I can't unit test it, since I cannot mock the Resolve() method. It is an extension method.

What's the best way to be able to mock it? The obvious thing would be to inject the IDataContext in the constructor instead of the ILifetimeScope, but for various reasons I cannot.

So, would it work to inject a Func instead?

public class MyService
{
    private readonly Func<IDataContext> _dbContext;

    public MyService(Func<IDataContext> dbContext)
    {
        _dbContext = dbContext;
    }

    public void DoWork()
    {
         var ctxt = _dbContext();
         // do stuff
    }
}

As in: I can mock it, but will Autofac figure out how to inject the correct parameter to the ctor? Or is there a better/simpler way to do this?

5 Answers

This is far from ideal but it's possible to examine the internals of the extension method and mock the actual non-extension calls it makes. Obviously this is fragile in the sense that a new version of autofac might break your tests.

For me, the following was successful (autofac v4.X):

Mock<IComponentRegistry> componentRegistryMock = new Mock<IComponentRegistry>();
IComponentRegistry componentRegistry = componentRegistryMock.Object;
IComponentRegistration componentRegistration = new Mock<IComponentRegistration>().Object;

componentRegistryMock
    .Setup(x => x.TryGetRegistration(It.IsAny<Service>(), out componentRegistration))
    .Returns(true);

lifetimeScopeMock
    .Setup(x => x.ComponentRegistry)
    .Returns(componentRegistry);

lifetimeScopeMock
    .Setup(x => x.ResolveComponent(componentRegistration, It.IsAny<IEnumerable<Parameter>>()))
    .Returns(dataService);

This worked for me:

var mock = AutoMock.GetLoose(builder => {
    var dbContext = new Mock<IDataContext>();
    dbContext.Setup(d => d.Something())
        .Returns(whatever...);

    builder.RegisterInstance(dbContext.Object).As<IDataContext>();
});

var myService = mock.Container.Resolve<MyService>();
Related