Configuration properties are not loaded in Spring Test - java

I have a Spring Boot application which has some configuration properties. I'm trying to write a test for some of the components and want to load configuration properties from a test.properties file. I can't get it to work.
Here's my code:
test.properties file (under src/test/resources):
vehicleSequence.propagationTreeMaxSize=10000
Configuration properties class:
package com.acme.foo.vehiclesequence.config;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
#Component
#ConfigurationProperties(prefix = VehicleSequenceConfigurationProperties.PREFIX)
public class VehicleSequenceConfigurationProperties {
static final String PREFIX = "vehicleSequence";
#NotNull
private Integer propagationTreeMaxSize;
public Integer getPropagationTreeMaxSize() {
return propagationTreeMaxSize;
}
public void setPropagationTreeMaxSize(Integer propagationTreeMaxSize) {
this.propagationTreeMaxSize = propagationTreeMaxSize;
}
}
My test:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = VehicleSequenceConfigurationProperties.class)
#TestPropertySource("/test.properties")
public class VehicleSequenceConfigurationPropertiesTest {
#Autowired
private VehicleSequenceConfigurationProperties vehicleSequenceConfigurationProperties;
#Test
public void checkPropagationTreeMaxSize() {
assertThat(vehicleSequenceConfigurationProperties.getPropagationTreeMaxSize()).isEqualTo(10000);
}
}
The test fails with "Expecting actual not to be null" meaning the property propagationTreeMaxSize in the configuration properties class was not set.

Two minutes after posting the question, I've found the answer.
I had to enable configuration properties with #EnableConfigurationProperties(VehicleSequenceConfigurationProperties.class):
#RunWith(SpringRunner.class)
#TestPropertySource("/test.properties")
#EnableConfigurationProperties(VehicleSequenceConfigurationProperties.class)
public class VehicleSequenceConfigurationPropertiesTest {
#Autowired
private VehicleSequenceConfigurationProperties vehicleSequenceConfigurationProperties;
#Test
public void checkPropagationTreeMaxSize() {
assertThat(vehicleSequenceConfigurationProperties.getPropagationTreeMaxSize()).isEqualTo(10000);
}
}

Related

Test on Spring configuration properties reading an external YML file doesn't work

I'm working with Spring Boot 2.4.8, and I'm reading into a bean the information read from an external YML file:
#Component
#ConfigurationProperties(prefix = "my.conf")
#PropertySource(value = "classpath:ext.yml", factory = YamlPropertySourceFactory.class)
public class MyExternalConfProp {
private String property;
public void setProperty(String property) {
this.property = property;
}
public String getProperty() {
return property;
}
}
I defined a custom factory to read external YML files, as stated here in the article #PropertySource with YAML Files in Spring Boot:
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(
Objects.requireNonNull(encodedResource.getResource().getFilename()),
Objects.requireNonNull(properties));
}
}
The content of the YML file is the following:
my.conf.property: yeyeye
The problem is that I cannot find a proper slice to test the configuration property in isolation. In fact, the following test fails:
#SpringBootTest(classes = {MyExternalConfProp.class})
class MyExternalConfPropTest {
#Autowired
private MyExternalConfProp confProp;
#Test
void externalConfigurationPropertyShouldBeLoadedIntoSpringContext() {
assertThat(confProp).hasFieldOrPropertyWithValue("property", "yeyeye");
}
}
As we said, the test fails with the following message:
java.lang.AssertionError:
Expecting
<in.rcard.externalconfprop.MyExternalConfProp#4cb40e3b>
to have a property or a field named <"property"> with value
<"yeyeye">
but value was:
<null>
Whereas, if I don't use any slice, the test succeeds:
#SpringBootTest
class ExternalConfPropApplicationTests {
#Autowired
private MyExternalConfProp confProp;
#Test
void contextLoads() {
assertThat(confProp).hasFieldOrPropertyWithValue("property", "yeyeye");
}
}
How can I resolve this? Is it some initializer or something similar that I can add to the slice to make the test succeed?
Here you can find the whole project on GitHub.
Add #EnableConfigurationProperties to your test or start the spring boot application on your test will solve the problem
#EnableConfigurationProperties
#SpringBootTest(classes = {MyExternalConfProp.class})
class MyExternalConfPropTest {
#Autowired
private MyExternalConfProp confProp;
#Test
void externalConfigurationPropertyShouldBeLoadedIntoSpringContext() {
assertThat(confProp).hasFieldOrPropertyWithValue("property", "yeyeye");
}
}
or
#SpringBootTest(classes = {YourSpringBootApplication.class})
class MyExternalConfPropTest {
#Autowired
private MyExternalConfProp confProp;
#Test
void externalConfigurationPropertyShouldBeLoadedIntoSpringContext() {
assertThat(confProp).hasFieldOrPropertyWithValue("property", "yeyeye");
}
}

Spring autowire of static configuration-object fails

as a Spring newbie I try to load external configuration into a Configuration-Object which I then try to autowire.
Following is my setup broken down to a bare minimum (less fields aso):
Package-structure:
app
|--Application.java
app.configuration
|--ConfigProperties.java
With my configuration.properties residing in:
src/main/resources/config
Application.java:
package app;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import app.configuration.ConfigProperties;
#SpringBootApplication
public class Application
{
//just to test if autowire works
#Autowired
public static ConfigProperties config;
public static void main(String[] args)
{
SpringApplication.run(Application.class, args);
System.out.println(config.getId());
}
}
ConfigProperties.java:
package app.configuration;
import javax.validation.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.validation.annotation.Validated;
#Configuration
#PropertySource("classpath:/config/configuration.properties")
#ConfigurationProperties
#Validated
public class ConfigProperties
{
#NotBlank
private String id;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
}
This results in a NullPointerException for the line:
System.out.println(config.getId());
in the Apllication.java.
I read several posts on this topic but most imply that own instantiation prevents autowiring, which I am not doing, or that the scan can't find the class to autowire, but from what I read with my annotations and package structure that should not be the problem.
Where is/are my mistake(s)?
PS: it seems that properties are read correctly from the property file. If I test the validation, the expected error is thrown for Null-value if leave out the part where I try to autowire.
#Autowired
public static ConfigProperties config;
You cannot autowire static fields in Spring.
//just to test if autowire works
You can write a new Junit test cases for that.
Something like:
#Autowired
public ConfigProperties config;
in your test class.
Since the annotation #Autowired requires getters/setters to inject the correct implementation, I don't suggest you to wire a static content. However, there is a workaround for this issue:
public static ConfigProperties config;
#Autowired
public Application(ConfigProperties config) {
Application.config= config;
}
Alternatively, take a look on the #PostConstruct annotation.
Do not use a static ConfigProperties . Then use #PostConstruct to print the ID from your config object.
#Autowired
ConfigProperties config;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#PostConstruct
void printId(){
System.out.println("ID = " + config.getId());
}

dynamically load test property file and service property file in spring boot

Writing junit for spring boot service class. My problem is, I am having two properties files(one for application and another for test) During junit i want to load test property file and during application, i want to load application properties file.But always it loads my application service properties file.
Service.java
#Component
#PropertySource("classpath:service.properties")
public class webModelService implements IWebModelService<webModel> {
#Value("${service.common.software.url}")
private String softwareEndPoint;
#Value("${service.common.software.url}")
private String createwebEndpoint;
#Value("${service.common.software.delete.url}")
private String deletewebEndpoint;
#Value("${service.common.thing.url}")
private String createthingEndPoint;
#Override
public void save(WebModel wModel) {
log.info("Save web model -> start");
System.out.println("softwareEndPoint===>"+softwareEndPoint);
System.out.println("createwebEndpoint===>"+createwebEndpoint);
System.out.println("deletewebEndpoint===>"+deletewebEndpoint);
System.out.println("createthingEndPoint===>"+createthingEndPoint);
}
}
Junit.java
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan("com.ericsson.tmo.iotep.dataimport")
#TestPropertySource("classpath:Test-service.properties")
#ContextConfiguration(classes = { BeansForDefaultValueGenerator.class }, loader = AnnotationConfigContextLoader.class)
public class webModelServiceTest {
#Autowired
webModelService webService;
#Test
public void testwebModelService(){
nwebModel.setNotes("Test_notes");
List<Software> softwareList = new ArrayList<>();
software.setSoftwareName("Test_software");
softwareList.add(software);
anwebModel.setSoftware(softwareList);
webService.save(anwebModel);
}
}
service.properties
service.common.software.url=http://192.168.99.100:8080/softwares
service.common.thing.url=http://192.168.99.100:8080/thing
service.common.software.url=http://192.168.99.100:8080/deviceModels
service.common.software.delete.url=http://192.168.99.100:8080/deviceModels/
Test-service.properties
service.common.software.url=http://localhost:8083/softwares
service.common.thing.url=http://localhost:8083/thing
service.common.software.url=http://localhost:8083/deviceModels
service.common.software.delete.url=http://localhost:8083/deviceModels/
And I need to load test-service.properties file during junit and i need to load service.properties during my applicartion run
your test properties file should be located in test folder (in resources)
if your properties file named by application.properties (application-{profile}.properties) and properties file for testing application-test.properties, spring boot load properties hierarchy will be : booting application.properties and then load application-test.properties file, spring overrides values in application properties from application-test properties.
(Spring properties)
if you want to tell spring where it should search properties filed for testing you could use something like that:
#TestPropertySource({"classpath:/application.properties",classpath:/application-test.properties"})
#ActiveProfiles(profiles = "test")
Use the PropertySourcesPlaceholderConfigurer in order to mention the property for service and override with test configuration when you run the test through Junit
Something like this
`
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import java.io.IOException;
#Configuration
public class PropertyTestConfiguration {
#Bean
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() throws IOException {
final PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocations(ArrayUtils.addAll(
new PathMatchingResourcePatternResolver().getResources("classpath*:application.properties"),
new PathMatchingResourcePatternResolver().getResources("classpath*:test.properties")
)
);
return ppc;
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class PropertyTests {
#Value("${elastic.index}")
String index;
#Configuration
#Import({PropertyTestConfiguration.class})
static class ContextConfiguration {
}
}
`

SpringBoot unit test configuration

I created a spring-boot 1.4.0 application and I would like to internationlize it using yaml file.
I created a class for loading the configuration from the yaml file like it is explained in the documentation here http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties.
I would like to create a test to check that my class has correctly loaded the properties from the yaml file.
If we keep the exemple from the documentation how to create a unit test that will load a yaml file (with a different name that application.yml) and check that the method getUsername() will return the value from the yaml file ?
Here is the code I have but still can't load the username :
#Component
#ConfigurationProperties(locations = "classpath:mylocalizedprops.yml", prefix="connection")
public class ConnectionProperties {
private String username;
// ... getters and setters
}
and the test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = Application.class)
public class InternationalizationTest {
#Autowired
private ConnectionProperties connectionProperties;
public void propsShouldBeNotNull() {
assertNotNull(connectionProperties);
}
public void userNameShouldBeCorrect() {
assertEquals(connectionProperties.getUsername(), expectedUserName);
}
}
I have failed the userNameShouldBeCorrect test. The file mylocalizedprops.yml is located in the src/main/resources folder of a Maven structured application.
I would consider this an integration test, not a unit-test because you are testing the interaction between various components. Regardless, here is how I would do it.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = YourApplication.class)
public class InternationalizationTests() {
#Autowired
ConnectionProperties connectionProperties;
#Test
public void testCorrectTranslationLoaded() {
Assert.assertEquals("english-username", connectionProperties.getUsername());
}
}
You can also create a test configuration if you would like to, which you can specify which translation to load. You would then need different classes to test different configurations. See the docs: http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
Unit test can be done easily with Jmockit
import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import mockit.Mock;
import mockit.MockUp;
import mockit.Mocked;
import mockit.Verifications;
class RuleApiApplicationTest {
#Mocked
private ConfigurableApplicationContext mockedContext;
#Test
void testApplicationRun() {
new MockUp<SpringApplication>() {
#Mock
public ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return mockedContext;
}
};
RuleApiApplication.main(new String[]{});
new Verifications() {{
SpringApplication.run(RuleApiApplication.class, new String[]{});
}};
}
}

#ComponentScan not working in test with spring-boot-starter-test

I am attempting to test my #Service and #Repository classes in my project with spring-boot-starter-test and #Autowired is not working for the classes I'm testing.
Unit test:
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes = HelloWorldConfiguration.class
//#SpringApplicationConfiguration(classes = HelloWorldRs.class)
//#ComponentScan(basePackages = {"com.me.sbworkshop", "com.me.sbworkshop.service"})
//#ConfigurationProperties("helloworld")
//#EnableAutoConfiguration
//#ActiveProfiles("test")
// THIS CLASS IS IN src/test/java/ AND BUILDS INTO target/test-classes
public class HelloWorldTest {
#Autowired
HelloWorldMessageService helloWorldMessageService;
public static final String EXPECTED = "je pense donc je suis-TESTING123";
#Test
public void testGetMessage() {
String result = helloWorldMessageService.getMessage();
Assert.assertEquals(EXPECTED, result);
}
}
Service:
#Service
#ConfigurationProperties("helloworld")
// THIS CLASS IS IN /src/main/java AND BUILDS INTO target/classes
public class HelloWorldMessageService {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message=message;
}
}
The commented class annotations on the unit test represent the various things I've tried to get this working. The test and the project packages are in the same package paths and the #ComponentScan works fine from my entry point (#RestController class with main method). The service #ComponentScan's and #Autowire's fine in my #RestController class in the src/main/java side, but does not in the test. I am required to add it again as a #Bean in my #Configuration class in order for #Autowired to work. The class is otherwise in scope just fine and I can reference and instantiate it just fine from the test. The problem appears to be that #ComponentScan does not appear to correctly traverse multiple entries in my test runner classpath, in this case /target/test-classes and /target/classes.
The IDE I am using is IntelliJ IDEA 13.
UPDATE - here are HelloWorldRs and its config:
#RestController
#EnableAutoConfiguration
#ComponentScan
public class HelloWorldRs {
// SPRING BOOT ENTRY POINT - main() method
public static void main(String[] args) {
SpringApplication.run(HelloWorldRs.class);
}
#Autowired
HelloWorldMessageService helloWorldMessageService;
#RequestMapping("/helloWorld")
public String helloWorld() {
return helloWorldMessageService.getMessage();
}
}
...
#Configuration
public class HelloWorldConfiguration {
#Bean
public Map<String, String> map() {
return new HashMap<>();
}
// This bean was manually added as a workaround to the #ComponentScan problem
#Bean
public HelloWorldMessageService helloWorldMessageService() {
return new HelloWorldMessageService();
}
// This bean was manually added as a workaround to the #ComponentScan problem
#Bean
public HelloWorldRs helloWorldRs() {
return new HelloWorldRs();
}
}
First, I'd recommend to use a newer #RunWith(SpringRunner.class) but that makes no difference, it is just shorter (and recommended).
Second, from the #EnableAutoConfiguration I see that you are using spring boot - which is certainly a good thing. There are some good reasons why not to use #ComponentScan directly. Can you try the following?
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes=YourApplication_or_other_Configuration.class)
public class HelloWorldTest {
... etc.
I don't know if this will turn out to be the solution, but don't use the default package (i.e. don't put *.java in "src/main/java" directly), and definitely don't use a #ComponentScan or #EnableAutoConfiguration in the default package. You will end up killing your application on startup as it tries to scan everything on the classpath (including all the Spring libraries).
SpringBoot 2.7.3, JUnit 5.8.2
If you want to have full control about the spring's configuration (and not rely on the hidden magic of auto configuration) I suggest to create an explicit configuration class:
#ComponentScan(basePackages = { "my.package.to.scan" })
public class MySpringTestConfig
{
// just for spring configuration annotations
}
and reference it in your test class:
#ContextConfiguration(classes = { MySpringTestConfig.class })
#ExtendWith({ SpringExtension.class })
class MySpringTest
{
...
}

Categories