Spring profiles not being set with gradle system property - java

I'm running the following command
./gradlew -Dspring.profiles.active=ci build
but when I do the profile is not set
INFO - The following 3 profiles are active: "test", "test-feign", "dev" : e1.configuration.feign.DevExchangeOAuth2FeignRequestInterceptorCalledTest
those are being set by application.properties
spring.profiles.active = dev
spring.profiles.include= test
spring.profiles.group.ci[0] = ci-feign
spring.profiles.group.test[0] = test-feign
why is this? and how can I fix it. Note: I cannot set the env var for reasons stupid in corporate environment. Also this is a monorepo.
UPDATE I felt like this was obvious, but I only want to set this in CI, not all the time. And it should respect configuration cache

System properties are only set for a single JVM.
You are setting the property for the JVM, which executes the Gradle Build itself.
Your tests, however, will be running in a different JVM (with different system properties).
Your can set system properties for your unit tests in your build.gradle as follows:
test {
systemProperty 'spring.profiles.active', 'ci'
}

Related

How does spring boot know which property file to use based on environment

I have 4 files in my project:
application.properties
application-dev.properties
application-qa.properties
application-prod.properties
application.properties has a property spring.profiles.active = #active.profile#
When running on local, it uses application-dev.properties file. But in UAT and Prod, it uses respective property files. My question is how does spring boot know to use dev when im running in local and and qa in uat and prod in prod?
What does #active.profile# mean?
This is decided by the "profiles" variable. This is a Set<String>.
The exact way spring detect the profiles depends on the way you run your application.
The most common way is through the System Parameter: -Dspring.profiles.active=dev. So I assume somwhere in your production enviroment this variable gets set.
Alternativelly, if you run your spring app via a builder, you can define the profiles explicitly (code is in kotlin):
SpringApplicationBuilder(MyApp::class.java)
.profiles(*profiles)
.run(*args)
Check this article for more info: https://www.baeldung.com/spring-profiles

How to autoselect springprofile when running tests

I am looking for a solution to automatically add the environment variable SPRING_PROFILES_ACTIVE="test" when running unit-tests. The solution should fulfill the following criteria :
Ideally it should be configured via maven pom.xml
If 1 is not possible configuration should be done for IntelliJ via configuration file in the project not via UI setting
The particular environment variable should only be set when running unit tests not when generally launching the app.
Any idea on how to approach this goal is appreciated.
Best
Andy
The SPRING_PROFILES_ACTIVE is a property value that should be set in a file like application-test.properties or application-test.yml
In a yml file it would look like,
spring:
profiles:
active: test
Additionally, there are specific annotations to help identify certain classes/methods as test specific such as #Profile("test") or #ActiveProfiles("test").

spring.profiles.active is not working in springboot application

I have created Profiles in Java class like this,
#Profile(value = "cache")
public class MapCache {
....
}
These profiles are not getting activated when spring.profiles.active used, but if i use spring.profiles.include profiles are working fine.
I would like activate profiles through properties which are added in application.properties
Note: application is running in independent jetty instance.
Any tip would be great on this.
To activate a profile via annotations you need #ActiveProfile("profileName"). The #Profile annotation is used to label an object as being a member of the profile.
you can also try to run the application and pass them as command line args
java -jar -Dspring.profiles.active={your_profile_name} application.jar
or if you run the app via maven:
mvn spring-boot:run -Dspring-boot.run.profiles={your_profile_name}
Here is an nice example I found on the internet with all the ways you can set the spring profile, it should help : https://www.baeldung.com/spring-profiles
I encountered a similar issue where SpringBoot wasn't activating the profiles set in the spring.profiles.active application property.
The issue was the result of the code base using a non standard name and location for the application property file (not my doing). Once I specified the location via the command line arg- --spring.config.location=/non/standard/location/acme.properties- the profile was activated.

Spring Autoconfig order/precedence on Profiles

I've just been reading through some of the Spring documentation and I have a question about the way the autoconfigs work. So if we run a Java app with specific profiles, it will automatically use profiles that are named in the format:
application-{{profileName}}.properties
So lets say I'm running the application with the profiles dev and personal. This means that the following properties files with be run:
application.properties, application-dev.properties, and application-personal.properties. I'm confused as to how spring determines the precedence of these profiles. Like, if I define something in dev that overwrites personal, which one will be used?
Thanks!
Spring Boot uses your defaut profile default then overrides it sequentially with the profiles you listed.
For example, if you specify in your application.properties :
spring.profiles.active=dev,personnal
key1=default-value1
key2=default-value2
key3=default-value3
All values defined in personnal profile will override those from dev and default.
application-dev.properties :
key2=dev-value2
key3=dev-value3
application-personnal.properties :
key3=personnal-value3
Your app will match theses values :
key1=default-value1
key2=dev-value2
key3=personnal-value3
You can also use spring.profiles.include in application-personnal.properties to unconditionnally include dev profile in personnal profile for example :
spring.profiles.include=dev
Regards
You can think of application.properties file as 'file of default values'.
When you specify a profile on bootstrap, for example 'dev', first of all, application.properties file is processed, after that, these values are overwritten by values imported from application-dev.properties file. So values on 'application-personal.properties' are not imported.

Spring integration tests with profile

In our Spring web applications, we use the Spring bean profiles to differentiate three scenarios: development, integration, and production. We use them to connect to different databases or set other constants.
Using Spring bean profiles works very well for the changing the web app environment.
The problem we have is when our integration test code needs change for the environment. In these cases, the integration test loads the application context of the web app. This way we don't have to redefine database connections, constants, etc. (applying the DRY principle).
We setup our integration tests like the following.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = ["classpath:applicationContext.xml"])
public class MyTestIT
{
#Autowired
#Qualifier("myRemoteURL") // a value from the web-app's applicationContext.xml
private String remoteURL;
...
}
I can make it run locally using #ActiveProfiles, but this is hard-coded and causes our tests to fail on the build server.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = ["classpath:applicationContext.xml"])
#ActiveProfiles("development")
public class MyTestIT
{ ... }
I also tried using the #WebAppConfiguration hoping that it might somehow import the spring.profiles.active property from Maven, but that does not work.
One other note, we also need to configure our code so that developers can run the web app and then run the tests using IntelliJ's test runner (or another IDE). This is much easier for debugging integration tests.
As other people have already pointed out, you can opt to use Maven to set the spring.profiles.active system property, making sure not to use #ActiveProfiles, but that's not convenient for tests run within the IDE.
For a programmatic means to set the active profiles, you have a few options.
Spring 3.1: write a custom ContextLoader that prepares the context by setting active profiles in the context's Environment.
Spring 3.2: a custom ContextLoader remains an option, but a better choice is to implement an ApplicationContextInitializer and configure it via the initializers attribute of #ContextConfiguration. Your custom initializer can configure the Environment by programmatically setting the active profiles.
Spring 4.0: the aforementioned options still exist; however, as of Spring Framework 4.0 there is a new dedicated ActiveProfilesResolver API exactly for this purpose: to programmatically determine the set of active profiles to use in a test. An ActiveProfilesResolver can be registered via the resolver attribute of #ActiveProfiles.
Regards,
Sam (author of the Spring TestContext Framework)
I had a similar problem: I wanted to run all of my integration tests with a default profile, but allow a user to override with a profile that represented a different environment or even db flavor without having to change the #ActiveProfiles value. This is doable if you are using Spring 4.1+ with a custom ActiveProfilesResolver.
This example resolver looks for a System Property, spring.profiles.active, and if it does not exist it will delegate to the default resolver which simply uses the #ActiveProfiles annotation.
public class SystemPropertyActiveProfileResolver implements ActiveProfilesResolver {
private final DefaultActiveProfilesResolver defaultActiveProfilesResolver = new DefaultActiveProfilesResolver();
#Override
public String[] resolve(Class<?> testClass) {
if(System.getProperties().containsKey("spring.profiles.active")) {
final String profiles = System.getProperty("spring.profiles.active");
return profiles.split("\\s*,\\s*");
} else {
return defaultActiveProfilesResolver.resolve(testClass);
}
}
}
And in your test classes, you would use it like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles( profiles={"h2","xyz"},
resolver=SystemPropertyActiveProfileResolver.class)
public class MyTest { }
You can of course use other methods besides checking for the existence of a System Property to set the active profiles. Hope this helps somebody.
If you want to avoid hard-coding the profile you may want to use the system property spring.profiles.active and set it to whatever you need in that particular environment e.g. we have "dev", "stage" and "prod" profiles for our different environments; also we have a "test", "test-local" and "test-server" profiles for our testing.
Remember that you can have more than one profile in that system property by using a list of comma-separated values e.g. "test,test-qa".
You can specify system properties in a maven project in the maven surefire plugin or passing them like this:
mvn -DargLine="-DpropertyName=propertyValue"
As #ElderMael mentioned you could use the argLine property of maven surefire plugin. Often when I need to run all the test with different specific Spring profiles I define additional maven profile. Example:
<profiles>
<profile>
<id>foo</id>
<dependencies>
<!-- additional dependencies if needed, i.e. database drivers ->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dspring.profiles.active=foo</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
With that approach you could easily run all the test with activated profile by maven command:
mvn clean test -Pfoo
The #ActiveProfile annotation is good but sometimes we need to run all the test with activated specific profiles and with hard-coded #ActiveProfile parameters it is a problem.
For example: by default integration test with H2 in-memory db, but sometimes you want to run test on the "real" database. You could define that additional maven profile and define Jenkins job. With SpringBoot you could also put additional properties to test/resources with name application-foo.yml (or properties) and those properties will be taken into account to.
there are many faces to this problem.
in my case, a simple addition to build.gradle already helped:
test { systemProperties = System.properties }

Categories

Resources