3

If I have these interfaces:

ISequencer = interface;
IController = interface;

The implementation for them (Controller requires Sequencer by contructor injection):

TSequencer = class(TInterfacedObject, ISequencer)
end;

TController = class(TInterfacedObject, IController)
  constructor Create(Sequencer: ISequencer);
end;

I register the implementations in the global container:

GlobalContainer.RegisterType<TSequencer>.Implements<ISequencer>;
GlobalContainer.RegisterType<TController>.Implements<IController>;

GlobalContainer.Build;

And finally, with the auto-wiring feature I can get a new instance of the IController interface:

Controller := ServiceLocator.GetService<IController>;

That's ok for the real application code. But in the test project I want to mock ISequencer. Depending on the test, when I ask the container for an implementation for ISequencer, sometimes I need the real implementation (TSequencer) and other times I need a mock implementation (like TSequencerMock). How can I do this switch?

Rafael Piccolo
  • 2,328
  • 1
  • 18
  • 29
  • As I mentioned several times already: using the ServiceLocator is not good. In this example we can see why. Because now you need to pass in some information if you want it to return the real thing or a mock. If you want to test the controller you might pass in a mock sequencer as you are testing the controller and not the sequencer. If you need to pass in a real sequencer you might ask yourself why you need it as you are testing the controller. Also: unit testing should not require the DI container at all. If you cannot do your dependency injection "by hand" you are doing it wrong. – Stefan Glienke Feb 11 '13 at 16:27

1 Answers1

4

You can register more than one implementation for a given interface. You then call them by name:

GlobalContainer.RegisterType<TSequencer>.Implements<ISequencer>('real');
GlobalContainer.RegisterType<TController>.Implements<IController>('mock');

And then you can call them by name as desired:

Controller := ServiceLocator.GetService<IController>('mock');

I wrote an article about how to do it here:

http://www.nickhodges.com/post/Getting-Giddy-with-Dependency-Injection-and-Delphi-Spring-9-%E2%80%93-One-Interface-Many-Implementations.aspx

Nick Hodges
  • 16,902
  • 11
  • 68
  • 130
  • 1
    Actually I already read that post of yours hehe... But is this compatible with the auto-wiring feature? I don't see how can I do it, because when I ask for an implementation for IController, I don't specify what kind of Sequencer I want, I just do: `Controller := ServiceLocator.GetService;`. Spring injects the implementation of ISequencer automatically. – Rafael Piccolo Feb 07 '13 at 17:29
  • You could pass a string variable based on compiler directives to determine which implementation is passed, 'test' or 'real'. – Nick Hodges Feb 09 '13 at 14:50
  • @NickHodges If the ServiceLocator.GetService code is part of the production code, there is no way to write 'real' or 'test' there. – Stefan Glienke Feb 11 '13 at 16:23