Integration Testing With Spring – Configurations

Gil Zilberfeld explains about Spring features for testing and integration testing, this time with configurations
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration testing.
Dependency InjectionConfigurationsNested configurations
Organizing Configurations

So how does Spring know what type to inject?

Spring can do so in a different ways. The default is scanning any class in the class path. Spring tries to match the types it finds in the class path to those needed by @autowired declarations, and if there’s a match, it will inject it.

However, this solution is limited. It doesn’t solve the interface problem – if there are multiple implementors, it still won’t have enough information to inject the right type. Also, what if we need to run it in different way? For example, in one test we need a LaserPrinter and in another the InkPrinter? In tests we would very much like this option.

The answer to “how can we help Spring know what to choose” is called a Configuration (Actually, a ConfigurationContext but we’ll keep it short.)

A Configuration is a set of all kinds of settings that Spring loads when it runs. Specifically, it can be a set of Beans, which are basically factories for injecting classes.

The old way to declare beans was XML-based, which was horrible to read, write and maintain. The modern way is with code. So if we want to inject a laser printer, we’ll set up a configuration class with a bean of the type Printer:

To make sure our test class uses this configuration we can tell it with the ConfigurationContext annotation:

When Spring loads the test class, it notices that Printer needs an injection. It looks into the specified PrinterConfiguration, and finds a @Bean that returns a Printer type. It then invokes the laserPrinter method in the configuration and puts the resulting object right there in the printer variable. Presto: An object was born.

There can be more than one

Configuration classes can come separate or nested, and can be combined in different ways. We can use the @Import annotation to include other configuration classes, . It is important to understand that the configurations are combined, they do not overwrite each other. That gives us better flexibility in separating configurations into manageable pieces. But that also means that they need to be managed like any code.

I know what you’re thinking: that’s ok to override the default injection, but what about the multiple implemenetors issue?

There are different ways to do this:

  • Use different configuration classes,each containing a single Bean for that type. Each test class will load the configuration it needs, not everything else.
  • Using @Primary on the bean we want to load, makes it the default in the configuration. This is great for testing if the default is declared in the application configuration, and we need to override it.
  • We can name a bean using @Bean(name=”beanName”) and then specify the bean to load with:@Autowired @Qualifier(“myBean”) in the injected classes

Working with configurations is not only a powerful way to, well, configure runs, but also a requirement in testing. Without configuration classes, we won’t be able to create different beans for different scenarios.

Sometimes we need a mock and sometimes a real object for the same type, the only other options is to create these objects ourselves in the test classes, which is missing the point in Spring. Plus, the code would be different – if we’re in charge of object creation, we need to pass around dependencies to the tested code. We’ll need different constructors or setters to pass these around. Basically we’re giving up on all the goodness Spring gets us.

Configuration classes help us solve this problem in tests, and we’ll see examples in the next posts.

 

Leave a Reply

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