簡體   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