views:

320

answers:

3

I'm testing some code that uses StructureMap for Inversion of Control and problems have come up when I use different concrete classes for the same interface.

For example:

[Test]
public void Test1()
{
    ObjectFactory.Inject<IFoo>(new TestFoo());

    ...
}

[Test]
public void Test2()
{
    ObjectFactory.Initialize(
        x => x.ForRequestedType<IFoo>().TheDefaultIsConcreteType<RealFoo>()
    );
    // ObjectFactory.Inject<IFoo>(new RealFoo()) doesn't work either.

    ...
}

Test2 works fine if it runs by itself, using a RealFoo. But if Test1 runs first, Test2 ends up using a TestFoo instead of RealFoo. Aren't NUnit tests supposed to be isolated? How can I reset StructureMap?

Oddly enough, Test2 fails if I don't include the Initialize expression. But if I do include it, it gets ignored...

A: 

Of course it doesn't reset between tests. ObjectFactory is a static wrapper around an InstanceManager; it is static through an AppDomain and as tests run in the same AppDomain this is why it is not reset. You need to TearDown the ObjectFactory between tests or configure a new Container for each test (i.e., get away from using the static ObjectFactory).

Incidentally, this is the main reason for avoiding global state and singletons: they are not friendly to testing.

From the Google guide to Writing Testable Code:

Global State: Global state is bad from theoretical, maintainability, and understandability point of view, but is tolerable at run-time as long as you have one instance of your application. However, each test is a small instantiation of your application in contrast to one instance of application in production. The global state persists from one test to the next and creates mass confusion. Tests run in isolation but not together. Worse yet, tests fail together but problems can not be reproduced in isolation. Order of the tests matters. The APIs are not clear about the order of initialization and object instantiation, and so on. I hope that by now most developers agree that global state should be treated like GOTO.

Jason
A: 

Yes, NUnit tests are supposed to be isolated and it is your responsibility to make sure they are isolated. The solution would be to reset ObjectFactory in the TearDown method of your test fixture. You can use ObjectFactory.EjectAllInstancesOf() for example.

AlexD
Right, that makes sense. But ObjectFactory.EjectAllInstancesOf<IFoo>() doesn't seem to work, whether in TearDown or right in Test2.
parcydarks
+1  A: 

If you must use ObjectFactory in your tests, in your SetUp or TearDown, make a call to ObjectFactory.ResetAll().

Even better, try to migrate your code away from depending on ObjectFactory. Any class that needs to pull stuff out of the container (other than the startup method) can take in an IContainer, which will automatically be populated by StructureMap (assuming the class itself is retrieved from the container). You can reference the IContainer wrapped by ObjectFactory through its Container property. You can also avoid using ObjectFactory completely and just create an instance of a Container that you manage yourself (it can be configured in the same way as ObjectFactory).

Joshua Flanagan
My ObjectFactory doesn't have either ResetAll() or a Container property...is that new in 2.5.3? ... But regardless, are you saying that it's better use dependency injection with an IContainer?
parcydarks
They are both available in the latest trunk build, which I've been using for a while - I am not sure if they were in the last release.And yes, for the places where you must do service location, I think it is better to do it from an injected IContainer, rather than calling the static ObjectFactory directly.
Joshua Flanagan