In Castle Windsor 3, override an existing component registration in a unit test

castle-windsorunit testing

I am attempting to use Castle Windsor in my automated tests like so:

On every test:

  • The Setup() function creates a Windsor container, registering default implementations of each component
  • The Test function access the components via the method IWindsorContainer.Resolve<T>, and tests their behavior
  • The TearDown() function disposes of the Windsor container (and any created components)

For example, I might have 15 tests which accesses components which indirectly results in the creation of an IMediaPlayerProxyFactory component. The SetUp function registers a good-enough implementation IMediaPlayerProxyFactory, so I don't have the maintenance burden of registering this in each of the 15 tests.

However, I'm now writing a test Test_MediaPlayerProxyFactoryThrowsException, confirming my system elegantly handles an error from the IMediaPlayerProxyFactory component. In the test method I've created my special mock implementation, and now I want to inject it into the framework:

this.WindsorContainer.Register(
                                 Component.For<IMediaPlayerProxyFactory>()
                                          .Instance(mockMediaPlayerProxyFactory)
                              );

But Windsor throws a Castle.MicroKernel.ComponentRegistrationException, with the message "There is already a component with that name."

Is there any way that I can make my mockMediaPlayerProxyFactory be the default instance for the IMediaPlayerProxyFactory, discarding the component that's already registered?


According to the documentation, Castle Windsor 3 allows for registration overrides, but I could only find one example:

Container.Register(
    Classes.FromThisAssembly()
        .BasedOn<IEmptyService>()
        .WithService.Base()
        .ConfigureFor<EmptyServiceA>(c => c.IsDefault()));

ConfigureFor is a method of the BasedOnDescriptor class. In my case I'm not using the FromDescriptor or BasedOnDescriptor.

Best Answer

There are two things that you have to do to create an overriding instance:

  1. Assign it a unique name
  2. Call the IsDefault method

So to get the example to work:

this.WindsorContainer.Register(
                             Component.For<IMediaPlayerProxyFactory>()
                                      .Instance(mockMediaPlayerProxyFactory)
                                      .IsDefault()
                                      .Named("OverridingFactory")
                          );

Because I plan to use this overriding patten in many tests, I've created my own extension method:

public static class TestWindsorExtensions
{
    public static ComponentRegistration<T> OverridesExistingRegistration<T>(this ComponentRegistration<T> componentRegistration) where T : class
    {
        return componentRegistration
                            .Named(Guid.NewGuid().ToString())
                            .IsDefault();
    }
}

Now the example can be simplified to:

this.WindsorContainer.Register(
                             Component.For<IMediaPlayerProxyFactory>()
                                      .Instance(mockMediaPlayerProxyFactory)
                                      .OverridesExistingRegistration()
                          );


Later Edit

Version 3.1 introduces the IsFallback method. If I register all my initial components with IsFallback, then any new registrations will automatically override these initial registrations. I would have gone down that path if the functionality was available at the time.

https://github.com/castleproject/Windsor/blob/master/docs/whats-new-3.1.md#fallback-components