简体   繁体   中英

Autofac Container Update and Mock dependencies

As part of integration testing I would like to wrap a registered class with Autofac so that I can track what happens on that class and redirect the operations to the original implementation.

In the following example, I create a first container which the real app container and then create a spyContainer.

The spyContainer should reuse the registeredInstance of NameRetriever as well as the WorldLogger but the WorldLogger should be injected a HelloLoggerSpy which itself should have been instanciated with the original 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()
}

Anyone knows how to achieve this here and how will this be possible in the future?

It looks like HelloLoggerSpy acts like the decorator pattern and Autofac has native support for such a pattern.

Instead of your custom registration for HelloLoggerSpy you can use :

builder.RegisterDecorator<HelloLoggerSpy, IHelloLogger>();` 

See Adapter and Decorators from the Autofac documentation for more information

You can't build a container multiple time but you can create a childlifetime scope and register stuff on this new stuff.

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

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