简体   繁体   中英

How to mock Autofaq interface with Moq

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?

I know doing this is not ideal, but in some more uncommon scenarios it may be necessary.

I had a situation that was a bit different. I have a method that needs to create a new context on demand, it is a console app so it's not like core's MVC that would begin a new lifetimescope per request. So I got the Autofac/Moq extension: http://autofac.readthedocs.io/en/latest/integration/moq.html

This will allow you to tell the container what instance to return for a particular interface.

Then you can pretty much do it like this:

[Fact]
public void Test()
{
    using (var mock = AutoMock.GetLoose())
    {
        var moqMockMe = new Mock<MockMe>();
        moqMockMe.Setup(s => s.AmIMocked()).Returns("Yes");
        mock.Provide(moqMockMe.Object);

        var lifeTimeScope = mock.Create<ILifetimeScope>();
        using (var scope = lifeTimeScope.BeginLifetimeScope())
        {
            var mockMeInsideScope = scope.Resolve<MockMe>();
            var actual = mockMeInsideScope.AmIMocked();
            Assert.Equal("Yes", actual);
        }
    }
}

public interface MockMe
{
    string AmIMocked();
}

public class MockMeImpl : MockMe
{
    public string AmIMocked()
    {
        return "No";
    }
}

So in case the service you're calling has a method that begins a new lifetimescope, you can mock the result and test it by using this fake ILifeTimeScope created in the example above.

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);

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>())) .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>();

For anyone else looking at this; the Func<T> trick worked for me.

Have you considered simply mocking the scope?

public class Test
{
    private readonly Mock<ILifetimeScope> _scopeMock;
    private readonly Mock<IDataContext> _contextMock;

    public Test()
    {
        _contextMock = new Mock<IDataContext>();
        _scopeMock = new Mock<ILifetimeScope>();
        _scopeMock.Setup( s => s.Resolve<IDataContext>() ).Returns( _contextMock.Object );
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM