Autofac 容器更新和 Mock 依赖项

Autofac Container Update and Mock dependencies

作为集成测试的一部分,我想用 Autofac 包装一个已注册的 class,这样我就可以跟踪 class 上发生的事情,并将操作重定向到原始实现。

在下面的示例中,我创建了第一个容器,它是真正的应用程序容器,然后创建了一个 spyContainer。

spyContainer 应该重用 NameRetrieverWorldLogger 的 registeredInstance,但是 WorldLogger 应该注入一个 HelloLoggerSpy,它本身应该用原文IHelloLogger.

public class NameRetriever
{
    public string GetName()
    {
        return "linvi";
    }
}

public interface IHelloLogger
{
    void Hello();
}

public class HelloLogger : IHelloLogger
{
    private readonly NameRetriever _nameRetriever;

    public HelloLogger(NameRetriever nameRetriever)
    {
        _nameRetriever = nameRetriever;
    }

    public void Hello()
    {
        Console.WriteLine("Hello " + _nameRetriever.GetName());
    }
}

public class WorldLogger
{
    private readonly IHelloLogger _helloLogger;

    public WorldLogger(IHelloLogger helloLogger)
    {
        _helloLogger = helloLogger;
    }

    public void World()
    {
        _helloLogger.Hello();
        Console.WriteLine("Welcome in this world");
    }
}

public class HelloLoggerSpy : IHelloLogger
{
    private readonly IHelloLogger _sourceHelloLogger;
    public bool Called { get; private set; }

    public HelloLoggerSpy(IHelloLogger sourceHelloLogger)
    {
        _sourceHelloLogger = sourceHelloLogger;
    }

    public void Hello()
    {
        _sourceHelloLogger.Hello();
        Called = true;
    }
}

static void Main()
{
    var containerBuilder = new ContainerBuilder();
    // This is normal container creation
    containerBuilder.RegisterInstance(new NameRetriever());
    containerBuilder.RegisterType<HelloLogger>().As<IHelloLogger>();
    containerBuilder.RegisterType<WorldLogger>();

    var realContainer = containerBuilder.Build();

    // This is something that would be invoked during tests
    // to override the A behaviour
    containerBuilder.Register<IHelloLogger>(context =>
    {
        var realA = context.Resolve<IHelloLogger>(); // recursive as IA is not yet reusing the previous one
        var aSpy = new HelloLoggerSpy(realA);
        return aSpy;
    });

    var spyContainer = containerBuilder.Build(); // cannot build twice

    var b = spyContainer.Resolve<WorldLogger>();
    b.World(); // should have called  HelloLoggerSpy.Hello()
}

任何人都知道如何在这里实现这一点以及将来如何实现?

看起来 HelloLoggerSpy 就像装饰器模式一样,Autofac 对这种模式有原生支持。

您可以使用 :

而不是 HelloLoggerSpy 的自定义注册
builder.RegisterDecorator<HelloLoggerSpy, IHelloLogger>();` 

有关详细信息,请参阅 Autofac 文档中的 Adapter and Decorators

您不能多次构建一个容器,但您可以创建一个 childlifetime 范围并在这个新内容上注册内容。

using(var scope = realContainer.BeginLifetimeScope(b => {
     b.RegisterDecorator<HelloLoggerSpy, IHelloLogger>(); 
}))
{
    scope.Resolve<IHelloLogger>(); // => HelloLoggerSpy
}