Integration Testing With Spring – Dependency Injection

Gil Zilberfeld explains Spring testing features starting with dependency injection
In this series we're taking at Spring and its features supporting testing in general, and specifically integration testing.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I
Mocking IIMocking III

I want to take a step back and discuss what Spring and Spring Boot are what are the basics features we get out of the box, and how we can use them for our advantage. There’s a whole training I’ve developed for Spring features for integration testing I do, so if you’re interested, ping me. We’ll get back to TDD-ing the hell out of it soon.

Spring started out as a dependency injection framework. “Dependency injection” is really a fancy name for something really simple, but valuable.
Let’s look at at this code of a PrinterManager class:

It’s obvious that our code is tied (or coupled) to the LaserPrinter class. What will happen if no laser printer is available? What if all we have is a whole garage full of dot matrix printers? Our code doesn’t help us there. So let’s change it to support any printer:

Holy Parameter Batman!

We now support multiple printers. I’ve used dependency injection in a way we call “passing a parameter”. Dependencies can be injected as parameters, with setter functions, or in Spring’s case, using annotations.

There’s a subtle thing we did here: We’ve separated the creation of the object (our LaserPrinter), from the use of it (inside our print method). The advantage we got is the ability to write code that doesn’t care which printer it operates.

But we can do a bit more. We may have a garage with ample supply of dot matrix printers, but a laser printer – we only got one. Now imagine that anyone created its own instance of the LaserPrinter object. Chaos ensues, but with the Printer as injected parameter the user code doesn’t know and doesn’t care.

Through the separation of creating the object and using it, we can now manage the created objects somewhere else, and without constraints from the users. We can create a LaserPrinter as a singleton, while we can create a DotMatrixPrinter as a multi-instance object, or even run a pool for clients to use. We can also do some security checks, without the client code knowing about it. For tests, we can inject mocks. Ah, the things we could do…

Wait, this is what Spring does. Spring default way of injecting objects is using the @Autowired annotation. If you see code that looks like this:

That means that when our PrinterManager loads, Spring identifies it has a dependency of type Printer, and it looks for objects to create from that type. All the PrinterManager needs to declare is the dependency type.

Spring’s advantage over passing a parameter is significant – regardless which calls I write, if it needs a Printer reference – it will get it, regardless where it is in the object chain. The alternative is pass parameters around from object to object until the dependency reaches its destination. That requires more code, more noise, and more testing. Ah it comes with more bugs.

Obviously this type of capability is a great for testability. We can’t inject any type of object we want, real ones, mocks – whatever we want – without changing the tested code.

Now the question is how does Spring know what to inject? What if Printer is an interface or an abstract class? or if there are multiple implementors of Printer? And how do we choose and tell Spring what we really really want?

We’ll discuss this next.

Leave a Reply

Your email address will not be published. Required fields are marked *