Domanda

I'm using StructureMap 2.6.4.0. My primary goal of using it is to simplify my testing. According to StructureMap documentation (here) it has a great feature for temporary mock injection (ObjectFactory.Inject<>()).

Correct me if I'm wrong but here is my understanding of that feature:

  1. You have your code for container configuration in one place with all "real" implementation mapped to their interfaces.
  2. Before every test run you initialize your ObjectFactory based on the same code as your application does.
  3. If any test needs to replace some "real" implementation with a "test" one it injects "test" implpementation with an Inject method on ObjectFactory.
  4. To restore original mapping and to remove injected mocks you need to call ResetDefaults().

And my problem is in 4th step.

Here is a code example, which figures my problem:

public interface IValueProvider
{
    string GetValue();
}

public class ValueProvider : IValueProvider
{
    public string GetValue()
    {
        return "Value provider";
    }
}

public class TestValueProvider : IValueProvider
{
    public string GetValue()
    {
        return "Test value provider";
    }
}

class Program
{
    static void Main(string[] args)
    {
        ObjectFactory.Initialize(x => x.For<IValueProvider>().Use<ValueProvider>());

        var valueProvider = ObjectFactory.GetInstance<IValueProvider>();
        Console.WriteLine(valueProvider.GetValue());
        // I will see expected "Value provider"


        ObjectFactory.Inject<IValueProvider>(new TestValueProvider());
        valueProvider = ObjectFactory.GetInstance<IValueProvider>();
        Console.WriteLine(valueProvider.GetValue());
        // I will see expected "Test value provider"


        ObjectFactory.ResetDefaults();
        valueProvider = ObjectFactory.GetInstance<IValueProvider>();
        Console.WriteLine(valueProvider.GetValue());
        // I will see NOT expected "Test value provider".
        // Doesn't ResetDefaults() have to restore a ValueProvider implementation?
    }
}

So, could anybody tell me where I'm wrong? Or maybe it is a bug in StructureMap?

È stato utile?

Soluzione

The ResetDefaults() method switches the profile to String.Empty. Which in your case it already was. This doesn't remove plugin types registered through Inject<T>(T object).

The Inject<T>(T object) method registers the object as the default for the PluginType "T". In other words it overrides your current default which was the concrete type ValueProvider.

The documentation of StructureMap is dated so my guess is that in earlier versions it worked the way it's described in the documentation but that is no longer the case.

So use the Inject<T>(T object) method only if you want to override the default of a configured plugin type at run-time or use StructureMap profiles.

StructureMap does provide some ways to remove "eject" types from the container. One of them is:

ObjectFactory.Model.EjectAndRemoveTypes(match =>            
            match != null &&
                   match == typeof (TestValueProvider)
);

This code does remove the registration made by the Inject<T>(T object) method but doesn't revert the default instance of type IValueProvider. This means there is no longer a default instance for IValueProvider.

Therefor ObjectFactory.GetInstance<IValueProvider>() will throw an StructureMap Exception Code: 202 No Default Instance defined for PluginFamily ... exception.

ObjectFactory.GetAllInstances<IValueProvider>().First() will give you back the instance of ValueProvider. The one you expect.

I am not really sure what your are trying to test here, but if they are unit-tests you should try to avoid the need for an IoC container (which also means depending on ObjectFactory). If you do need an container use an abstraction of IContainer which you can manage and create yourself. Some links to other SO questions backing this are:

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top