Angular's dependency injection makes it really easy to inject services into components and other services. In most cases, that's all we need. But what if we need more than one implementation of the same service contract? To accomplish this in strongly typed languages, we typically create interfaces as contracts that can be implemented by multiple services. When an instance of the interface is requested, the correct implementation is injected based on the dependency injection configuration. In Angular, this does not work.
Posts about Dependency Injection
By default, services in Angular are provided at the root module level. This way, the same instance of the service will be injected into any component depending on it. If a component needs a separate instance of the service for itself and its children, it can change the scope by declaring a service provider itself. However, this change also affects dependency injection in tests.
A while ago I've already written a blogpost on how to inject a different Angular service implementation based on a runtime value. With that approach, the selected service was initialized at startup and remained the same for the entire application lifetime. In response to that blogpost, I received a question how one could switch between the implementations while the application is running. This blogpost is my detailed answer to that question.
I keep getting impressed by how feature-rich dependency injection in Angular is. This time I needed it to inject the appropriate implementation of a dependency based on runtime information. Of course, the scenario is well supported.
Angular has an impressive dependency injection system, however some aspects could be documented better. Old blog posts explaining how things worked before the final release don't help either. Hence, it took me a while to successfully intercept HTTP requests and inject a common parameter.
The general advice is to avoid using IoC containers in your test code altogether. Unfortunately achieving that when IoC usage is being retrofitted into an existing application, can be challenging. Failing to do that will quickly result in having to reconfigure your IoC container for some of the tests.
One of the main reasons for using an IoC container like Ninject is to be able to inject different dependencies into your classes in production code and in test code. Usually you don't even need to use an IoC container in your tests, but when you longer lived dependencies will usually be scoped differently in test code than they are in production code.
Most of the time there's no need to use an IoC container in unit tests. Still, testing the IoC container configuration makes sense in an application to avoid runtime errors which will occur when not all required dependencies for the created object are registered in the application beforehand. This doesn't necessarily mean, the right implementations are being used for all dependencies, but that's not what we want to test.