Mocking Spring Boot Configurations Using Dependency Injection
kana -
Overview
In a proper web service, configurations are externalized to a property file to make the value consistent among usages and prevents the shotgun surgery anti-pattern. In Spring Boot, this is done by using the application.properties or the application.yml file.
Values from the property file can be injected to the beans by annotating the bean's property to set its value from the one from the property file, or annotating the constructor parameter just like how we do autowire-by-constructor.
For example, let us look at this YML file and how to inject its value to the beans.
The problem with this setup, mocking this class is relatively hard. Searching for solutions online will lead you to org.springframework.test.util.ReflectionTestUtils.
Using ReflectionTestUtils looks clean, but what happens under the hood is really dirty. There's no cleanup code and might affect data of other tests; remember the "I" in the Test F.I.R.S.T. principle: Tests should be independent.
Injecting using Autowire
This is a simpler approach, and take properly takes advantage of dependency injection. The only problem here is that the syntax is subjectively uglier than using @Value to a property rather than the constructor parameter.
One bad thing about this format is Mockito's @InjectMocks is not possible anymore since you cannot really mock strings. In general, mocking final class is not possible like the String class. That is why we are just passing strings as the mocked dependency.
Configuration Properties
Configuration property is a bean that abstracts the property file of the application. This is a better method to inject values from the property file.
When using a configuration property bean, all property keys are contained in a single class. Unlike using @Value annotation where you need to write the property key to all its usages. Additionally, configuration property beans will fail to initialize if the key is unmappable from the property file.
Installing Dependency
Its dependency, spring-boot-configuration-processor, must be installed in the project:
Creating Configuration Property Bean
Just create a bean with @ConfigurationProperties annotation:
When creating a configuration property bean, can be made with these easy steps:
Step 1 – Create the configuration class
Create a concrete class. For some reason, interfaces doesn't work, and make sure the class is not final. Remember that final classes are not easily mockable. In the example above, EncryptionConfig is the class that will hold the property values.
When attempting to create a configuration property bean in Kotlin using the data class construct, a bean can be built but it cannot be mocked since Kotlin data classes are final. To work around this, make the config class implement an interface:
Step 2 – Tell Spring which configuration property to bind to the class
Using the @ConfigurationProperties annotation, Spring will see the annotated class a property configuration bean. To determine which object from the property file to bind to the bean, just set the annotation's value to the key of property object to bind.
In the example, "my-app.encryption" is the key of the property object to bind.
Step 3 – Declare the property keys as the class properties
The class properties will have the same name as its keys from the property file. In the example above, algo is mapped to String algo and key is mapped to String key. When the key has multiple words, such as client-id and client-secret, just write the kebab-cased keys to camel case. Therefore in the example, client-id is written as clientId, and client-secret is written as clientSecret.
When mapping a nested object, another class should be created to map the internal object. The nested object does not need to be annotated with @ConfigurationProperties.
It is possible to map a YML array to Java list:
It is also possible to map YML map to a Java Map.
Step 4 – Make the class discoverable by component scan
The easiest part. Annotate the class with @Component.
Now that the class is a bean, it is now usable just like any Spring beans
Mocking a Configuration Property Bean
Since the configuration has been turned into a bean, it is now possible to mock it just like any beans or components in Spring Boot.
Mockito Example
MockK Example
Spock Example
Conclusion
While there are multiple ways of injecting a value from the properties file to the class that uses it, such as using the @Value annotation to the field or the constructor parameter, prefer to use a configuration property bean.
Configuration property bean enables to developer to create an abstraction layer for the property file. With this, the properties are usable just like any Spring bean. You can think of it as any repository interface, but it interfaces the property file rather than a data table.
Now that the configuration now works like any other bean, it is now mockable just like any other bean!