3

In my tests I setup an autofac container, it returns some real implementation and some mocks (DB, external systems).

The problem is that after each test I Dispose the container and create a new one: Autofac.IContainer.Dispose() and Container = builder.Build(); The already registered instances are still there.

How can I reset the container so it would be 'like new' again?

The reason why I want to do is - I want to replace one mocked instance with another. It's being registered as Singleton.

---- EDIT

Thanks for the answers. I decided to add some more code and describe what actually I'm trying to achieve. But that is actually a topic for another (prabably already answered question - unit testing CQRS).

My app contains static IContainer property:

public static IContainer Container { get; private set; }

After each test execution I create it again by calling those two methods:

public static ContainerBuilder Compose(IEnumerable<DependencyProvider> dependencyProviders)
{
    var collection = dependencyProviders as List<DependencyProvider> ?? dependencyProviders.ToList();
    var included = new HashSet<DependencyProvider>(collection);
    var includedTypes = new HashSet<Type>(collection.Select(x => x.GetType()));
    var currentWorkingSet = new List<DependencyProvider>(collection);

    while (true)
    {
        var candidates = currentWorkingSet.SelectMany(x => x.GetDependencies());
        var typesToBeAdded = candidates.Where(x => !includedTypes.Contains(x)).Distinct().ToList();
        if (typesToBeAdded.Any() == false)
            break;

       currentWorkingSet.Clear();
       foreach (var type in typesToBeAdded)
       {
            includedTypes.Add(type);
            var instance = CreateInstance(type);
            included.Add(instance);
            currentWorkingSet.Add(instance);
       }
    }

return BuildContainer(included);
}

and

TestDependencyProvider dependencyProvider = new TestDependencyProvider()
var builder = Compose(new[] { dependencyProvider });
Container = builder.Build();

The TestDependencyProvider is created for each test and contains moqed instances. It registers those mocks and x.GetDependencies() uses the original container registrations i.e. container.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsClosedTypesOf(typeof(IAggregateBusinessRuleForEvent<,>));

The type I'm mostly interested in is one of implementations of IAggregateBusinessRuleForEvent<,>).

public class RuleA: IAggregateBusinessRuleForEvent<AEvent, Something>
{
    private readonly IDependency _dependency;

    public RejectCompanyNameRule(IDependency dependency)
    {
         _dependency = dependency;
    }
}

So even though I create this container again that RuleA is still there and all of my test are using same instance with same _dependency :/

It's still not entierly clear why code looks how it looks, I'm trying to understand it by adding tests...

------- EDIT 2

Following Jimmy's advice I've implemented a sample using Update me

public interface IExample<T>
{
    void Hello();
}

public interface IExampleDependecy
{
    void SaySomething();
}

public class Example : IExample<string>
{
    private IExampleDependecy _dependecy;

    public Example(IExampleDependecy dependecy)
    {
        _dependecy = dependecy;
    }

    public void Hello()
    {
        Console.WriteLine("Hello");
        _dependecy.SaySomething();
    }
}

[TestMethod]
public void T()
{
    // first test
    var builder = new ContainerBuilder();
    builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsClosedTypesOf(typeof(IExample<>));
    var mockA = new Moq.Mock<IExampleDependecy>();
    mockA.Setup(d => d.SaySomething()).Callback(() => Console.WriteLine("A"));

    builder.RegisterInstance(mockA.Object).SingleInstance();
    var container = builder.Build();

    var sample1 = container.Resolve<IExample<string>>();
    sample1.Hello();



    // new test using same container
    var updater = new ContainerBuilder();
    builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsClosedTypesOf(typeof(IExample<>));
    var mockB = new Moq.Mock<IExampleDependecy>();
    mockB.Setup(d => d.SaySomething()).Callback(() => Console.WriteLine("B"));
    builder.RegisterInstance(mockB.Object).SingleInstance();
    updater.Update(container); // overwrites existing registrations

    var sample2 = container.Resolve<IExample<string>>();
    sample2.Hello();
}

and result is:

Hello
A
Hello
A
Karpik
  • 322
  • 1
  • 5
  • 14
  • Why are you using a DI container in your tests and not a Mocking framework (ex: Moq)? – vidalsasoon Sep 11 '13 at 18:43
  • I'm not 100% sure I'm doing it right but the scnario here is a CQRS, Event Sourcing MVC app. My test scenario is to execute one command and Assert weather is was executed correctly - Moq object was called. The command is handled by a handler, handler is registered in IoC and it's dependency it's injected by constructor. Problem here is that even after Dispose, handler is not created again but still lives in the container. – Karpik Sep 11 '13 at 19:32
  • You may have some valid reasons for using DI containers in your unit test but it's not the "correct" way to do it. http://stackoverflow.com/questions/2305893/what-should-be-the-strategy-of-unit-testing-when-using-ioc – vidalsasoon Sep 11 '13 at 19:44
  • Yep. You're absolutely right. Those are really integration tests, executing code in many classes using CQRS framework. I could easily unit test my `RulaA` class but I just don't see the point :/ All methods in those classes have one-two statements - is there a point to unit test them? http://stackoverflow.com/questions/4869633/applying-cqrs-is-unit-testing-the-thin-read-layer-necessary – Karpik Sep 11 '13 at 20:54

1 Answers1

2

Autofac by default overrides previous registrations with subsequent ones. That means you don't have to do anything special apart from updating container with your new instance:

var builder = new ContainerBuilder();
builder.RegisterInstance(new Sample("A")).SingleInstance();
var container = builder.Build();
var sample = container.Resolve<Sample>();
// do your test

...

// new test using same container
var updater = new ContainerBuilder();
updater.RegisterInstance(new Sample("B")).SingleInstance();
updater.Update(container); // overwrites existing registrations
var sample = container.Resolve<Sample>(); // returns "B"
k.m
  • 30,794
  • 10
  • 62
  • 86
  • I wrote a sample and it didn't work :/ I've added the test and result to the question. – Karpik Sep 11 '13 at 21:29
  • @Karpik: it does work -- in the `// new test ...` part of your code you need to call `.Register` methods on `updater`, **not on** `builder`, exactly like I did in my sample. – k.m Sep 21 '13 at 22:17