Add a spring profile to the existing ones in a SpringBootTest - java

I have a SpringBootTest and I want to add a spring-profile to the existing ones for a specific tests. I tried #SpringBootTest(webEnvironment = RANDOM_PORT, properties = "spring.profiles.include=my-second-profile") which was suggested in https://github.com/spring-projects/spring-boot/issues/7668 but this doesn't work at all. Only the profile which was included is active now. As a result my context does not start because required properties from the default active profiles are missing. Has anyone an idea how to solve this problem?
Thanks!

properties expects an array String[] not a single object String.
#SpringBootTest(webEnvironment = RANDOM_PORT, properties = {"spring.profiles.include=my-second-profile"})
Surround your String with {} and it should work.

The approach that works for me is using the separate #ActiveProfiles annotation, and not necessarily use the properties as you do. #ActiveProfiles expects a comma-separated string of profile names. So your annotations could become as follows:
#SpringBootTest(webEnvironment = RANDOM_PORT)
#ActiveProfiles(profiles = "main-profile,my-second-profile,other-profile")

Related

Spring #Value always returns a null value

I'm facing an issue with a dev I have to do with Spring (not spring boot).
In a class, I have to retrieve the value of a property defined on a properties files (application.properties).
So, I use the #Value annotation (normally it's easy to use), but it doesn't work in my case.
In my class, I have this code :
#Value("${value:'DefaultValue'}")
private String myValue;
with the import : import org.springframework.beans.factory.annotation.Value;
My property file contains : value=a value
But, when I want to test my application, I always receive a null value (also with the use of the default value).
I try to add deafferents annotations in my class like
#Component
...
Do you have any suggestion? I'm pretty sure I miss a config somewhere, but I don't know what.
Thanks in advance
Tigrou
Every class where you use #Value annotation MUST be a spring-managed bean. You can achieve this by adding #Component annotation but make sure that you have configured component scan #ComponentScan(basePackages = "your.package") in your java configuration, so spring knows where to search for components.
Finally, with the help of everybody here (special thanks to #M. Deinum I fixed my issue.
So, first thing done, replace the Junit dependeny (use) by a Spring test dependency.
secondly, I've updated my test to be executed with Spring with those lines :
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {AppConfig.class})
This was enough to retrieve the default value of the #Value field.
To retrieve the value I had to add
#PropertySource("classpath:application.properties")
in my AppConfig class

Spring boot override application properties in test

If I have a appplication.properties like:
url=someUrl
user=userOne
password=ABCD
But if I want to be able to set the password when testing to something else, lets say:
password=someTest
How do I do that?
I need to do this in one test
#Test
void checkSomething{
//change/override password before calling someMethod only for this test
someMethod();
}
You can create a testing profile file something like application-testing.properties and specify the overridden properties there.
Now while running the application you can specify use profile using
-Dspring.active.profiles=testing
There are multiple ways.
1st way: Spring Profile
aplication.yaml:
spring.profiles.active=dev
---
spring.profile=dev
url=someUrl
user=userOne
password=ABCD
---
spring.profile=test
password=someTest
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
public class MyTestClass {...
2nd way: SpringBootTest Properties
#RunWith(SpringRunner.class)
#SpringBootTest(properties = { "password=someTest" })
public class MyTestClass {...
create another application.properties under src/test/resources thats all you need,
if you want to get properties to use in one method you can do i without involving spring :
InputStream input = Main.class.getResourceAsStream(yourproperties-path);
Properties prop = System.getProperties();
prop.load(input);

How to override application-test.yml in spring boot test?

I have src/main/test/resources/application-test.yml, as per SpringBootTest it will load application.yml and then loads application-test.yml. But I face a scenario where I want to override certain properties in application-test.yml only for one test but other test needs to use properties from application-test.yml. How would I do this ?
I tried to use the #TestPropertySource annotation to override but it is not working.
#Slf4j
#RunWith(SpringRunner.class)
#SpringBootTest(classes= MyApplicationTestApplication.class)
#ActiveProfiles("test")
#DirtiesContext
#TestPropertySource(locations = {"classpath:application-test.yml",
"classpath:file-test.properties"})
Thanks for comments and answer , just wanted to add which worked for me
#SpringBootTest(properties = "some.property=localhost:9094")
Link to doc
What about creating one more profile and activate both of them (order matters) #ActiveProfiles({"test", "test-override"})
Or you can just override using System.properties, for example in static block, before spring context starts loading itself.

Spring boot properties to be loaded at initialization and respect all and control #Aspect based on the value from property file

We are loading properties from an external file using #PropertySources. Now I want to enable/disable #Aspect based on a property. I tried using #ConditionalOnExpression which didn't work. I tried the same by creating a bean of propertyplaceholderconfig. Even in the same case, it didn't work. Then I tried #profile which also didn't work initially.
What I Figured out is that these variables are not initialized at the starting when propertysource or propertyplaceholder bean is used at startup. Some variables are always ignored like (logging.file). But #Value works fine. In order to set these variables, I've to pass them as JVM parameters.
So my questions are:
1. How can I make spring to always read specified property files at startup and respect all of them?
2. Which is the best way to enable/disable #Aspect. Using #profile or #ConditionalOnExpression or something else?
Currently, we are setting logging.file in the main method since this also behaves the same way. But you guys know that it's not the proper way as I may end up adding the properties one by one like this. I want to put all the properties into external files such that spring reads those files and sets its properties.
Our properties structure:
common.properties #This has all common properties
service.properties #Property specific to a service. This will also contain existing property from common.properties which will be overridden.
I understand that I can use profiles. But, we want to keep the properties outside such you need to restart service if you are changing the properties. I also don't want to pass the variables as JVM parameters then I've to pass most of the variables in this way. Passing -Dspring.config.location is also difficult as common.properties and service.properties are used and 'service.properties' filename varies for each service.
sample codes:
Mainclass:
#PropertySources({
#PropertySource(value = "file:${property_path}/common.properties", ignoreResourceNotFound = false),
#PropertySource(value = "file:${property_path}/service1.properties", ignoreResourceNotFound = true) })
public class MainClass {
static String logDirectory = ApplicationContext.getGlobalProperty("logging.file");
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MainClass.class);
Properties properties = new Properties();
properties.put("logging.file", logDirectory);
springApplication.setDefaultProperties(properties);
springApplication.run(args);
}
}
Application Context:
#Configuration
#EnableAutoConfiguration
public class ApplicationContext implements EnvironmentAware {
private static Environment environment;
#Override
public void setEnvironment(Environment environment) {
ApplicationContext.environment = environment;
}
public static String getGlobalProperty(String propertyName) {
return environment.getProperty(propertyName);
}
}
Here you can see any way I've used environment to get property. Is there any way to set the property using the environment such that while spring boot initialization itself the properties are populated?
We can also implement ApplicationContextInitializer and override initialize method to read properties. But how can I make it read 2 property files and override the duplicate property with the latest value? Reference(I'm not sure how to implement my requirements in this way.). Even in this case doesn't sound like you are trying to kill a mosquito with a hammer?
Current working Solution:
#Aspect
#Profile("!production")
#Configuration
public class ControllerAspect {
#pointcut(....)
} //Here also I've to pass spring.profiles.active as JVM params.
//setting the value in common.properties or service1.properties is not working.
I'm a newbie to spring boot so please let me know for additional clarifications.
It seems Spring by default loads some properties at initialization and unless until you specifically write logic to overwrite them (like the one I wrote in MainClass.java) there is no option to override those. Some of these include (logging.file, key used in #ConditionalonExpression).
Some tricks with their own challenges:
Specify the properties in application.properties in your classpath. The variables loaded at the earlier stages are always read from this file. challenge: I've tight coupled all my properties into the jar and in order to change the values I've to recompile and relaunch the Jar.
Use profiles and define application.properties as application-profile.properties. challenge: I've to create so many profiles and still the previous challenge exists.
Pass the property value as JVM parameter as -Dproperty.key=value. challenge:seriously? How many properties am I supposed to send as JVM parameter?
Implement ApplicationContextInitialize and override initialize method.challenge:Overriding Spring's default behaviour is not recommended as well as isn't it an overkill to use this just for reading property file?
Solution:
Use -Dspring.config.location to specify the property files. In this case, always spring reads the properties only from the specified location(s). You can provide multiple property files as well. Refer this for much more details. It seems if you give property locations as Directories spring loads them in reverse order. But if you specify files it follows the order specified.
Note: All these can be combined together. To know about precedence refer this.

Specifying classes loading order in #ContextConfiguration in JUnit test cases

I am using junit 4.11 and spring-test-4.1.5 for writing integration test cases. I am using #ContextConfiguration annotation to load the configurations.like,
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes ={ApplicationConfig.class,SpringConfiguration.class }, initializers = {CustomProfileResolver.class, AppInitializer.class}, loader = SpringApplicationContextLoader.class)
As you can see i am using two configuration classes.
The problem i am facing is i want to load the SpringConfiguration.class before ApplicationConfig.class. I tried with reversing the order but it didn't worked. Neither #order can be specified here.
So how to order the loading of these configuration classes, is it possible ?
#ContextConfiguration and its supporting classes (e.g., MergedContextConfiguration, AnnotationConfigContextLoader, AbstractTestContextBootstrapper, etc.) are intended to retain the original ordering of #Configuration classes declared via the classes attribute of #ContextConfiguration.
Thus, if you can verifiably prove that reversing the order of your SpringConfiguration and ApplicationConfig classes does not result in SpringConfiguration being processed first, then you have found a bug in Spring.
If you have found a bug, please report it in JIRA.
Thanks,
Sam
Use #ContextHierarchy
#ContextHierarchy({
#ContextConfiguration(classes = {SpringConfiguration.class}),
#ContextConfiguration(classes = {ApplicationConfig.class})
}

Categories