问题
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
Testfunction access the components via the methodIWindsorContainer.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.
回答1:
There are two things that you have to do to create an overriding instance:
- Assign it a unique name
- Call the
IsDefaultmethod
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
回答2:
Don't reuse your container across tests. Instead, set it to null in the TearDown() and re-initialise it for each actual test.
来源:https://stackoverflow.com/questions/9253388/in-castle-windsor-3-override-an-existing-component-registration-in-a-unit-test