Search code examples
c#.netunity-containerspecflowacceptance-testing

Using dependency injection in SpecFlow step-file


We're using Unity as our dependency injection framework.

I want to create an acceptance test and need an instance of DossierService.
Unfortunately I get the following exception:

BoDi.ObjectContainerException: 'Interface cannot be resolved [...]'

[Binding]
public class DossierServiceSteps : BaseSteps
{
    private IDossierService dossierService;

    public DossierServiceSteps(IDossierService dossierService)
    {
        this.dossierService = dossierService;
    }
}
  • What exactly is BoDi? I can't find any useful information..
  • How can I tell SpecFlow to use the normal Unity container?

Thanks in advance

Edit: I've tried using SpecFlow.Unity like so:

public static class TestDependencies
{
    [ScenarioDependencies]
    public static IUnityContainer CreateContainer()
    {
        var container = UnityConfig.GetConfiguredContainer();

        container.RegisterTypes(typeof(TestDependencies).Assembly.GetTypes().Where(t => Attribute.IsDefined(t, typeof(BindingAttribute))),
            WithMappings.FromMatchingInterface,
            WithName.Default,
            WithLifetime.ContainerControlled);

        return container;
    }
}

In UnityConfig the types are correctly registered

container.RegisterType<IDossierService, DossierService>(new InjectionConstructor(typeof(IDataService), typeof(IDossierRepository), typeof(IDbContext), true));

But I still get the same exception. When I put a breakpoint at the start of the CreateContainer() method of TestDependencies it doesn't break...


Solution

  • We solved this problem by implementing SpecFlow RuntimePlugin. In our case it was Castle.Windsor, but principle is the same. First you define the plugin which override default SpecFlow Instance Resolver:

    public class CastleWindsorPlugin : IRuntimePlugin
    {
        public void Initialize(RuntimePluginEvents runtimePluginEvents, RuntimePluginParameters runtimePluginParameters)
        {
            runtimePluginEvents.CustomizeScenarioDependencies += (sender, args) =>
            {
                args.ObjectContainer.RegisterTypeAs<CastleWindsorBindingInstanceResolver, IBindingInstanceResolver>();
            };
        }
    }
    

    Where in CastleWindsorBindingInstanceResolver we needed to implement single method: object ResolveBindingInstance(Type bindingType, IObjectContainer scenarioContainer);. This class contains container and resolution (in your case instance of IUnityContainer. I recommend to inject to the container instance of self, so that you could inject the instance of IUnityContainer to SpecFlow binding classes)

    This plugin needs to be in separate assembly and you load that to your test project like adjusting app.config like this:

    <specFlow>
        <plugins>
          <add name="PluginAssemblyName" path="." type="Runtime" />
        </plugins>
    ...
    </specFlow>