I am attempting to use Castle Windsor in my automated tests like so:
On every test:
Setup()
function creates a Windsor container, registering default implementations of each componentTest
function access the components via the method IWindsorContainer.Resolve<T>
, and tests their behaviorTearDown()
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?
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
.
There are two things that you have to do to create an overriding instance:
IsDefault
methodSo 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() );
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
Don't reuse your container across tests. Instead, set it to null
in the TearDown()
and re-initialise it for each actual test.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With