How are components removed with Castle 3.0?
Asked Answered
T

2

8

I have IWindsorContaner which exists for the whole application lifetime. For Unittests it is possible to register mocks/stubs, etc. under their Type. When the test is finished and the fixture is disposed the registered components in forthe test are remove with a self created method called "Unregister".

Now, I want to update to the latest Castle version which is 3.0. According to the 3.0 release notes something like

public void Unregister(string contextName, string registrationName)
{
   IWindsorContainer context = GetOrCreateContext(contextName);
   context.Kernel.RemoveComponent(registrationName);
}

is not possible anymore, because the IKernel.RemoveComponent method has been removed. The description to fix this is not really sufficient ("Try utilizing IHandlerSelectors.").

A simplified version of the fixture I use for unittests:

public sealed class DependencyInjectionFixture : IDisposable
{
  private Stack<Type> registeredTypes = new Stack<Type>();

  // Registering of mocks/stubs, etc
  public void RegisterSingleton<T>(T singleton, string objectName)
  {
     registeredTypes.Push(typeof(T));

     IWindsorContainer context = GetOrCreateContext(contextName);

     context.Register(Component.For(typeof(T))
                               .Named(objectName)
                               .Instance(singleton)
                               .LifeStyle.Singleton);
  }

  // Called when tests ends
  public void Dispose()
  {
     IWindsorContainer context = GetOrCreateContext(contextName);

     while (registeredTypes.Count > 0)
        context.Kernel.RemoveComponent(CSApplicationContext.GetRegistrationNameFor(registeredTypes.Pop()));
  }

}

How can I remove components with Castle 3.0?

Trend answered 29/2, 2012 at 14:48 Comment(0)
B
2

Instead of trying to remove all components, just create a new IWindsorContainer and bind that to whatever GetOrCreateContext is checking against. Then you'll have a fresh new container that has nothing bound to it.

Breathing answered 8/3, 2012 at 2:3 Comment(6)
Of course this would work. But this would also require a lot of code changes in many dependent software projects. I found a solution with putting an holder object to the container instead of the real one. But the more I think about it Castle does not really seem to be the right IoC container for unittests, is it?Trend
For all unit tests that I have written to this point, Castle has been exactly the perfect IoC container for unit tests. Why do you need to clear all of the registered entities anyway?Breathing
Because each unit test registers its own mocks. If they are not removed the next unit test would be using the wrong mocks. So there's code in the IDisposable.Dispose method of the Ioc test fixture that removes all components.Trend
Then each of the tests should probably have its own IWindsorContainer. Maybe you need to do away with GetOrCreateContext() for unit testing and just manually handle that container object.Breathing
Yes, but this would require a lot of changes in many dependends software products we have. So I guess the workaround is the only acceptable solution.Trend
Your answer does not explain the way to remove a componentSwaddle
B
1

I know Im late to the game but I came up against this same issue. My problem was that due to the size of the application, first run of Castle Windsor was very slow (1s+) and to create a new context for each and every test was becoming onerous (600 tests, 5min wait time on Castle Windsor only)

I based my solution on How to remove component in Castle Windsor 3.0?

    public class WindsorHandlerOverride : IHandlerSelector
{
    private Dictionary<Type, object> definedTypeBehaviours;

    public bool HasOpinionAbout(string key, Type service)
    {
        return definedTypeBehaviours.IsNotNullAndAny(t => t.Key == service);
    }

    public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
    {
        var theValue = definedTypeBehaviours[service];
        return new WindsorSimpleHandler {TheValue = theValue};
    }

    public void CleanUp()
    {
        definedTypeBehaviours = null;
    }

    public void OverrideBehaviour(Type type, object value)
    {
        if (definedTypeBehaviours == null)
        {
            definedTypeBehaviours = new Dictionary<Type, object>();
        }
        definedTypeBehaviours.Add(type, value);
    }
}

and

    public class WindsorSimpleHandler : IHandler
{
    public object TheValue { get; set; }
    public ComponentModel ComponentModel { get; set; }
    public HandlerState CurrentState { get; set; }

    public bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model,
        DependencyModel dependency)
    {
        return true;
    }

    public object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model,
        DependencyModel dependency)
    {
        return TheValue;
    }

    public object Resolve(CreationContext context)
    {
        return TheValue;
    }...

Then wherever you set up windsor

        WindsorOverrider = new WindsorHandlerOverride();
        container.Kernel.AddHandlerSelector(WindsorOverrider);

And when you want to override castle windsor default behaviour in test

WindsorOverrider.OverrideBehaviour(typeof(IService), mock.Object);

And on test tear down call

WindsorOverrider.CleanUp();
Bloat answered 4/5, 2018 at 1:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.