I have a files structure like this:
- main/resources
- application.yml
- externalApi.yml
- application-dev.yml
- externalApi-dev.yml
- test/resources
- application-test.yml
- externalApi-test.yml
Now in ApiWebClient class I want to use this additional file "externalApi.yml" based on active profile. I used #PropertySource like this:
#PropertySource(value = "classpath:externalApi-${spring.profiles.active}.yml", factory = YamlPropertySourceFactory.class)
public class ApiWebClientImpl implements ApiWebClient {
and everything looks fine, application starts. The problem starts in the tests:
#SpringBootTest(classes = TestApplication.class)
#EnableConfigurationProperties
#ConfigurationPropertiesScan(basePackages = "com.xyz.testapplication")
#ActiveProfiles(value = "test")
public class ApiWebClientTest {
When I ran it with maven (with "test" profile or without it) like this: mvn clean install -Ptest I've got error message that says
java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.profiles.active' in value "classpath:externalApi-${spring.profiles.active}.yml
So I've added it manually (#ActiveProfiles somehow does not work):
#SpringBootTest(classes = AdapterTestApplication.class, properties = "spring.profiles.active=test")
and this is working properly but I don't want to update all of the spring boot tests with this part properties = "spring.profiles.active=test". What if I will have like 200 tests? How to do it automatically? Why it does not take proper config file when I run it? Maybe there is way to get rid off ${spring.profiles.active} in annotation?
Related
I am using Hashi Corp vault in Spring Boot project. I am able to run the project without any issue. But when I run unit tests, secret-id and role-id are not being passed. I tried the following but got an exception saying both are empty. Tried hard coding the values, that didn't work either
EmployeeTest.java
#RunWith(SpringRunner.class)
#DataJpaTest
#ActiveProfiles(value = "ide")
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class EmployeeTest
{
private final Logger logger= LoggerFactory.getLogger(getClass());
#Autowired
EmployeeRepository employeeRepository;
#Test
public void getEmployeeById()
{
Employee employee=employeeRepository.getOne(13L);
logger.info(employee.toString());
}
}
Update:
I am able to pass secret-id and role-id through VM arguments but still properties are not resolving
Okay, it turns out when using profile from src/main/resources/application-ide.yml in spring boot test, properties are not being replaced by vault vaules. Copying the same file to src/test/resources/application-ide.yml fixes the issue.
TL;DR
For Spring Boot testing always better to use properties file from src/test/resources rather than src/main/resources
I have a test clas with
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class Foo{
...
}
which should start up a regular application context as defined by:
#SpringBootApplication(scanBasePackages = {"de.foo", "de.bar"})
public class Application {
...
}
This works as expected. Further I have an application.yml which gets loaded in both cases but when running the test, the property for JMX (spring.jmx.enabled) does not get loaded or it does not get used.
I tried different property files (application.yml, application-test.yml) but the only thing what works is setting the property via
#TestPropertySource(properties = "spring.jmx.enabled:true")
The property defaults to true in a regular application context.
Several questions:
Why is the default different in a test class?
Why does the property not get loaded or recognized, when loading it from an application.yml (the rest of the yml works, so it does get loaded).
This seems to be a known behavior, as seen in this comment in Spring Boot Sample Data Tests. Is there any documentation I missed about this behavior?
I've recently encountered the same situation myself, and have opened spring-projects/spring-boot#13008 to document this behavior. As a result, the following additions to the reference manual will be added in the upcoming 1.5.13.RELEASE and 2.0.2.RELEASE:
As the test context framework caches context, JMX is disabled by default to prevent identical components to register on the same domain. If such test needs access to an MBeanServer, consider marking it dirty as well:
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "spring.jmx.enabled=true")
#DirtiesContext
public class SampleJmxTests {
#Autowired
private MBeanServer mBeanServer;
#Test
public void exampleTest() {
// ...
}
}
How to create project architecture to support multiple envionment. Each environment will have different datasource from different property file like(dev-propertfile,test-propertyFil,Production-propertyfile) with help of spring's
org.springframework.core.env.Environment;
I'll give step by step procedure for Spring boot applications.
Inside /src/main/resources/application.properties mention spring.profiles.active=dev (or Prod)
Create /src/main/resources/application-dev.properties and give your custom dev configurations here.
Create /src/main/resources/application-prod.properties and give your custom prod configurations here.
Run.
Put property file in same location as application.property and follow
the naming convention application-{profile}.properties like
application-dev.properties,application-test.properties,
application-prod.properties
And in application.properties set spring.profiles.active=dev,test etc
For Spring Boot applications it will work easily even by using a YAML File
spring:
profiles: dev
property: this is a dev env
---
spring:
profiles: prod
property: this is a production env
---
However, for a Spring MVC application, it needs more work. Have a look at this link
Basically, it involves 2 steps
Get the Spring Profile within servlet context
If you have set the profile on the server and want it to retrieve it within your application you can use System.getProperty or System.getenv methods.
Here is the code which fetches the profile and defaults it to a local profile, if no profile has been found.
private static final String SPRING_PROFILES_ACTIVE = "SPRING_PROFILES_ACTIVE";
String profile;
/**
* In local system getProperty() returns the profile correctly, however in docker getenv() return profile correctly
* */
protected void setSpringProfile(ServletContext servletContext) {
if(null!= System.getenv(SPRING_PROFILES_ACTIVE)){
profile=System.getenv(SPRING_PROFILES_ACTIVE);
}else if(null!= System.getProperty(SPRING_PROFILES_ACTIVE)){
profile=System.getProperty(SPRING_PROFILES_ACTIVE);
}else{
profile="local";
}
log.info("***** Profile configured is ****** "+ profile);
servletContext.setInitParameter("spring.profiles.active", profile);
}
To access the application-dev.properties, say now you will need to use
#Profile("dev") at the class level
The following code will fetch the application-dev.properties and common.properties
#Configuration
#Profile("dev")
public class DevPropertyReader {
#Bean
public static PropertyPlaceholderConfigurer properties() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[] { new ClassPathResource("properties/common.properties"), new ClassPathResource("properties/application-dev.properties") };
ppc.setLocations(resources);
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
}
For accessing say application-prod.properties you have to use #Profile("prod") at the class level. More details can be found here
Take a look at Spring Profile. You will define a set of profiles configurations, like Test, Dev, Production. And then, when you launch the application, you can define wich profile it should use.
Here are some tutorials of how to use.
And this guys had the same problem as yours: How to config #ComponentScan dynamic?
We wanted a way to load different properties from application-<your_env>.properties file depending on the environment (spring profile) in a Spring MVC project, so we implemented a configuration class something like this.
#Configuration
#PropertySource({ "classpath:application-${envTarget:dev}.properties" })
#Data
public class EnvironmentConfig {
#Value("${files.s3.accessId:}")
String s3AccessId;
#Value("${files.s3.accessToken:}")
String s3AccessToken;
.
.
.
}
Then we loaded the EnvironmentConfig in the class where we needed to use it.
While running the application, you just need to pass the -DenvTarget=<your_env>, and it will pick up the application-<your_env>.properties file from src/resources folder of the project.
In the above code, it will load values from application-dev.properties files when no envTarget is specified.
Thanks to Karthikeyan Muthurangam for suggesting this clever solution.
I am creating simple Spring apps using Maven and have 2 config and properties. The hierarchy is:
- package.main
- App.java
- AppConfig.java
- app.properties
- package.main.model
- ModelConfig.java
- model.properties
App.java
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getEnvironment().getActiveProfiles()[0]);
}
AppConfig.java
#Configuration
#Import(ModelConfig.class)
#PropertySource("classpath:/package/main/app.properties")
public class AppConfig {}
app.properties
spring.profiles.active = prod
ModelConfig.java
#Configuration
#PropertySource("classpath:/package/main/model/model.properties")
#ComponentScan
public class ModelConfig {}
model.properties
spring.profiles.active = dev
Why the model.properties is override the app.properties (the result is dev)?
How to make application.properties like in Spring Boot that cannot overrode by new properties?
it is by the order of beans defined being ModelConfig loaded it will cause the active profile to be set for 'dev'.
BTW, looking at your setup, configuring the active profile of beans via properties file like this not the good idea though, spring.profiles.active should be specified in
application.properties
file, not to loaded via separate property files, so when application starts it loads the bean correctly for the profile
or via command line arguments when you invoke
-Dspring.profiles.active
or programmatically
context.getEnvironment().setActiveProfiles("live");
some good examples here
I'm trying to setup the database schema and some test data with liquibase for some tests. Each test has a separate changelog which setup the schema and some specific data for the test.
In order to make my tests working, I need to drop the schema before each test and fill it with new test data. However, it seems that this is not working because some tests are failing because the old test data is still available. I think something with my configuration is not correct. How can I force liquibase to drop the schema before each test?
My tests look as following:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyTestConfig.class)
#TestPropertySource(properties = "liquibase.change-log=classpath:changelog/schema-with-testdata.xml")
public class MyRepositoryTest {
The config for the tests looks as follows:
#SpringApplicationConfiguration
#Configuration
#EnableAutoConfiguration
#ComponentScan("com.mypackage")
#EntityScan(basePackages = { "com.mypackage.domain" })
#EnableJpaRepositories(basePackages = { "com.mypackage.domain", "com.mypackage.infra.persistence" })
public class MyTestConfig {
And the application.properties under src/main/test/resources is
liquibase.drop-first=true
spring.jpa.hibernate.ddl-auto=none
There is a spring.liquibase.dropFirst config property. Maybe this is what you're looking for?
Not sure if this completely answers your question. There is another property liquibase.default-schema=schemaNameToCreate
But even with that I was never able to get it to create the schema from scratch.