4

Within my Web API I have linked Autofac as IoC container, and I do it like this:

Domain level

public class Autofac
{
    protected ContainerBuilder Builder { get; set; }

    public Autofac()
    {
        this.Builder = new ContainerBuilder();
    }

    public virtual IContainer Register()
    {
        // Register dependencies

        SetUpRegistration(this.Builder);

        // Build registration.
        var container = this.Builder.Build();

        // End
        return container;
    }   

    private static void SetUpRegistration(ContainerBuilder builder)
    {
        // === DATALAYER === //

        // MyRepository
        builder.RegisterType<MyRepository>()
            .As<IMyRepository>()
            .InstancePerLifetimeScope();

        // === DOMAIN === //

        // MyManager
        builder.RegisterType<MyManager>()
            .As<IMyManager>()
            .InstancePerLifetimeScope();
    }
}

Web API

public class Autofac : Domain.IoC.Autofac
{
    public IContainer Register(HttpConfiguration config)
    {
        // Register your Web API controllers.
        base.Builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        // OPTIONAL: Register the Autofac filter provider.
        base.Builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);

        // Complete registration and get container instance.
        var container = base.Register();

        // Set the dependency resolver to be Autofac.
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        // Done.
        return container;
    }
}

As you see it inherits from the base class from Domain and sets up Web API specific config.

Usage

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    GlobalConfiguration.Configure(WebApiConfig.Register);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    new IoC.Autofac().Register(GlobalConfiguration.Configuration);
}

Which is at global.asax, as you know.

The question

This works fine for Web API, but I haven't got a clue what I need to do to register all this within a UnitTest project context.

The idea is that I would create a similar implementation to the Autofac class at Web API level, but than with mocks (completely ignoring the base class from Domain).

Any pointers?

Spikee
  • 3,967
  • 7
  • 35
  • 68
  • 3
    Personally I never see the need to ever (and do not know why it would even be viable) setup my IoC Container within a unit test. A unit test should be for testing a logical piece of code that is quick and easily tested on it's own without requiring the remainder of the application to have previously been setup. I think you are looking at the problem the wrong way and should be testing each section individually instead of as a whole. – Stephen Ross Jan 05 '16 at 10:55
  • @StephenRoss: Agreed at a theoretical level, but I need to test domain managers who have a dependency on `IRepositories` that will access the DB. I need to mock those so I'm not actually doing a delete all ... I think it's hard to avoid. – Spikee Jan 05 '16 at 10:57
  • 3
    In that case you should setup (in your unit test with mocks, as you've already said) only the dependencies that will be required for your domain managers. Remember you aren't testing that the domain managers work with all your `IRepositories` you are testing that the flow of data would end up in a `IRepository`. Then have separate tests for each of your `IRepositories` to make sure that they do as they should. I would certainly hope that your domain managers do not have a dependency on the remainder of the application. – Stephen Ross Jan 05 '16 at 11:01
  • 1
    Well as you have separated out your different repositories with an interface this will let you test your domain manager easily. If the repository itself inherits from `DBContext` it's a little more difficult, on the other hand if you take a `DBContext` as a dependency of the class I usually wrap it in an interfaced class allowing you to simply pass the interface and the class will simply provide a thin wrapper, easy to test and relatively simple. – Stephen Ross Jan 05 '16 at 12:18
  • @StephenRoss: I'm sorry, I deleted my update because I was thinking along the lines of what you say. Could you provide such a wrapper as an answer (plus your original statement to be complete) though please? That's something that I know insufficiently. – Spikee Jan 05 '16 at 12:24
  • 1
    http://stackoverflow.com/a/1465896/126014 – Mark Seemann Jan 05 '16 at 13:35

1 Answers1

1

Personally I never see the need (and I struggle to comprehend how viable or helpful it would be) to setup my IoC container directly within a unit test. As a unit test is used to test a logical piece of code that can be quickly built, easily ran and doesn't require much (I'd advocate no) tear-down. It should not require all of your application to be be setup for the test to run.

Remember that your unit test is simply testing the flow of data through the system i.e that your DomainManager is actually going to call a IRepository when you expect that it should. Then you would have separate test classes for all your repositories to determine that they would correctly add to the database etc.

I'm not sure how you use the DBContext class but as an example of a wrapper this is what it would sort of look like.

interface IDBSetWrapper
{
    object Add(object entity);
}

interface IDBContextWrapper
{
    ...
    IDBSet Set(Type entityType);
    ...
}

class DBContextWrapper : IDBContextWrapper
{
    private readonly DBContext context;

    public DBContextWrapper()
    {
        context = new DBContext();
    }

    ...

    public IDBSet Set(Type entityType)
    {
        var dbSet = context.Set(entityType);
        return new DBSetWrapper(dbSet);
    }

    ...
}

It's not much but I hope that it demonstrates what I mean about a thin wrapper. Basically the wrapper is the DBContext and will contain an instance of it within the class, the actual DBContext will be called when you request the wrapper to do anything. I have shown what would happen when returning another object (in this case a DBSet), this will also be wrapped in a separate object with an interface. This is so that you can mock the returns from this class easily.

You can add this new wrapper into your IoC a little better now as it provides an interface.

One thing to note is that you won't be able to and probably wouldn't wish to test the wrapper class, there would be very little point as I see it. But previously I've seen colleagues do an integration test on these sort of classes.

Stephen Ross
  • 882
  • 6
  • 21