繁体   English   中英

如何使用 Moq 模拟 Autofaq 界面

[英]How to mock Autofaq interface with Moq

使用 C#/Autofac/Moq:我有这个类:

public class MyService
{
    private readonly IlifetimeScope _scope;

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

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

这在运行时有效。 但是,我无法对其进行单元测试,因为我无法模拟 Resolve() 方法。 它是一种扩展方法。

能够模拟它的最佳方法是什么? 显而易见的事情是在构造函数中注入 IDataContext 而不是 ILifetimeScope,但由于各种原因我不能。

那么,它可以代替注入 Func 吗?

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

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

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

如:我可以模拟它,但是 Autofac 会弄清楚如何将正确的参数注入到 ctor 中吗? 或者有没有更好/更简单的方法来做到这一点?

我知道这样做并不理想,但在一些更不常见的情况下可能有必要。

我的情况有点不同。 我有一个方法需要根据需要创建一个新的上下文,它是一个控制台应用程序,所以它不像核心的 MVC 那样会为每个请求开始一个新的生命周期范围。 所以我得到了 Autofac/Moq 扩展: http ://autofac.readthedocs.io/en/latest/integration/moq.html

这将允许您告诉容器为特定接口返回什么实例。

然后你几乎可以这样做:

[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";
    }
}

因此,如果您正在调用的服务有一个开始新生命周期范围的方法,您可以模拟结果并使用在上面的示例中创建的这个假 ILifeTimeScope 来测试它。

这远非理想,但可以检查扩展方法的内部结构并模拟它所做的实际非扩展调用。 显然,这很脆弱,因为新版本的 autofac 可能会破坏您的测试。

对我来说,以下是成功的(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);

这对我有用:

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

对于其他正在看这个的人; Func<T>技巧对我Func<T>

你有没有考虑过简单地嘲笑范围?

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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