6

this is partially a duplication of the same question which has not been yet answered. See here: How can I override a component registered in Castle Windsor?

Since I cannot comment or post any answers to an existing questions I created this question again in the hope that somebody knows the answer to a seemingly basic and simple question.

Keep in mind that:

  1. I do not want to create a new container.
  2. I do not care if containers should not be used for Unit testing.
  3. I do not want to use derived containers.

If Castle Windsor is not able to provide this simple functionality, what other container implementation would you recommend?

Community
  • 1
  • 1
Radek Strugalski
  • 560
  • 7
  • 22
  • I believe your problem arises from two of your "limitations" - #1 and #2. If you are indeed *unit* testing, then a container will just get in your way - you have one piece of *code under test* and your dependencies should all be mocked. If you are *integration* testing, then it may make sense to use a container. But in that case, *you should use a different container than your application* and mock anything that is not relevant for the test. [Reusing a container](http://blog.ploeh.dk/2015/01/06/composition-root-reuse/) is a bit like reusing a `.config` file for a different app - why would you? – NightOwl888 Jun 13 '16 at 19:29
  • 1
    @NightOwl888: This is to be used for Integration tests. New container does not make sense as e.g. I would like to tests everything apart from the log4net component. Or everything apart from DB access layer. If I create a new container I am not testing the real subject but something else which is not the point. Copy/paste of the container mapping code is also a not the best practice (seen so often). – Radek Strugalski Jun 14 '16 at 11:29
  • Possible duplicate of [How can I override a component registered in Castle Windsor?](http://stackoverflow.com/questions/1687574/how-can-i-override-a-component-registered-in-castle-windsor) – Krzysztof Kozmic Aug 24 '16 at 11:33

4 Answers4

8

Windsor works with convention that "first registration wins". But, if you have not SPECIFICALLY told it that this component will be overridden, it will throw an exception. So, there are 2 ways to allow existing component to be be overridden:

  1. Register component with .IsDefault(). This will override existing registration.
  2. Register component with .IsFallback(). This will allow component to be overridden later.
  3. Using unique name for component - .Named("NewComponentName").

I personally prefer .IsDefault() and use this shorthand extension in my integration tests:

    public static class WindsorContainerExtensions
    {            
        public static void Override<TService>(this IWindsorContainer container, TService instance) where TService : class
        {
            container.Register(Component.For<TService>().Instance(instance).IsDefault());
        }
    }
Andrew Harry
  • 13,773
  • 18
  • 67
  • 102
andree
  • 3,084
  • 9
  • 34
  • 42
  • IsFallback and IsDefault seems like a kind of a solution. Bad thing about it that you need to change the subject being tested, that is you need to register your components in a specific way so they can be tested later. Eventually I went with Ninject and its Bind/Unbind functionality. Windsor seems to be lacking basic functionality so I had to made this decision. – Radek Strugalski Mar 30 '17 at 11:18
  • You don't need to change subject being tested. .IsDefault() is used in unit tests, when you wan't to replace component. – andree Mar 30 '17 at 11:47
  • 9
    Windsor most definitely does *not* use a "last registration wins" convention. This answer led me down a rabbit hole because I took it as correct :/ [link](https://github.com/castleproject/Windsor/blob/master/docs/registering-components-one-by-one.md#user-content-register-more-components-for-the-same-service) "In Windsor first one wins: In Castle, the default implementation for a service is the first registered implementation. This is different from AutoFac for example, where the default is the last registered implementation" – mr_jrt Mar 21 '18 at 18:17
  • The above answer was incorrect as stated; there are only two ways to to allow the overriding of components: 1) use `IsFallback` for the original registration and `IsDefault` for the new one (which can no longer be overridden); or 2) use named components. A dependency without IsFallback cannot be overridden (not even by using `IsDefault`), and a dependency with `IsFallback` can only be overridden by using `IsDefault`. – Mike Rosoft Mar 10 '20 at 13:43
  • Okay, there is a third way: Register a named dependency, and set *that* to `IsDefault()`. – Mike Rosoft Mar 10 '20 at 14:13
  • Please change '2 ways' back to to '3 ways'. (I have suggested an edit, then found that it wasn't correct, tried to cancel it by undoing all changes and just editing the formatting (Stack Overflow doesn't allow directly canceling a suggested edit), and forgot to undo the change of number. – Mike Rosoft Mar 10 '20 at 16:41
1

I have no knowledge about other containers but Caslte so my answer is about Castle. If you want to replace what you can do is write an extension method to the IWindsorContainer that will remove and then add.

But I think you should rethink a bit your design:

  1. Why does your class need direct access to the container and try to resolve from it by itself?
  2. Why are you in need to change your source code for test code? If writing clean Dependency Injection code according to SOLID your tests will really "magically" flow.

Can you please explain more about the design and about the relevant classes?

Gilad Green
  • 36,708
  • 7
  • 61
  • 95
0

Answering to a second part of the question - what containers support registration overriding.

Ninject.

See Bind()/Unbind() methods.

I tried also Autofac but seems that the registration becomes frozen after it being built. So seems that it may not be also possible with Autofac.

Radek Strugalski
  • 560
  • 7
  • 22
0

Register your service like this, and then the other registration in your tests will overwrite it.

Container.Register(Component.For<ISomething>().ImplementedBy<RealSomething>().IsFallback());

Worthy7
  • 1,455
  • 15
  • 28